From 5bb5017e764f9acbb4de3452ddc0c0eef8a0c2b4 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 28 Mar 2023 15:23:58 +0200 Subject: [PATCH 1/8] [Synthetics] Disable edit policy (#149506) ## Summary Fixes https://github.com/elastic/kibana/issues/137505 Shows deprecation notice when attempting to edit legacy integrations [Elastic-Synthetics---Integrations---Elastic (1).webm](https://user-images.githubusercontent.com/11356435/228042467-9b9a593a-a713-4a38-aa6a-1c083e82f3c5.webm) ### Testing 1. Check out the 8.5 branch 2. Create a legacy Elastic Synthetics integration 3. Check out this branch 4. Navigate to the legacy Elastic Synthetics integration 5. Observe the deprecation notice --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dominique Clarke --- .../fields/script_recorder_fields.tsx | 4 +- .../browser/advanced_fields.test.tsx | 21 +- .../fleet_package/browser/advanced_fields.tsx | 84 +- .../fleet_package/browser/normalizers.test.ts | 78 -- .../fleet_package/browser/normalizers.ts | 115 -- .../browser/script_recorder_fields.tsx | 6 +- .../fleet_package/browser/simple_fields.tsx | 22 +- .../browser/source_field.test.tsx | 74 +- .../fleet_package/browser/source_field.tsx | 203 +-- .../browser/throttling_fields.test.tsx | 2 +- .../browser/zip_url_tls_fields.test.tsx | 108 -- .../browser/zip_url_tls_fields.tsx | 98 -- .../contexts/policy_config_context.tsx | 23 - .../fleet_package/custom_fields.test.tsx | 14 +- .../fleet_package/helpers/normalizers.ts | 42 - .../fleet_package/hooks/use_policy.ts | 4 +- .../hooks/use_update_policy.test.tsx | 680 ---------- .../fleet_package/hooks/use_update_policy.ts | 67 - .../http/advanced_fields.test.tsx | 2 +- .../fleet_package/http/normalizers.ts | 88 -- .../fleet_package/icmp/normalizers.ts | 31 - ...ics_edit_policy_extension_wrapper.test.tsx | 361 +++++ .../synthetics_policy_create_extension.tsx | 2 +- ...hetics_policy_create_extension_wrapper.tsx | 7 +- .../synthetics_policy_edit_extension.tsx | 48 - ...ics_policy_edit_extension_wrapper.test.tsx | 1178 ----------------- ...nthetics_policy_edit_extension_wrapper.tsx | 133 +- .../fleet_package/tcp/normalizers.ts | 44 - .../fleet_package/validation.test.ts | 76 -- .../components/fleet_package/validation.tsx | 160 --- .../edit_monitor_config.tsx | 6 - .../monitor_management/validation.test.ts | 5 +- .../monitor_management/validation.ts | 21 +- .../integration_deprecation.test.tsx | 2 +- .../integration_deprecation_callout.tsx | 4 +- .../pages/monitor_management/add_monitor.tsx | 1 - .../translations/translations/fr-FR.json | 24 - .../translations/translations/ja-JP.json | 24 - .../translations/translations/zh-CN.json | 25 - 39 files changed, 428 insertions(+), 3459 deletions(-) delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.test.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.test.tsx delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.tsx delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/normalizers.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.test.tsx delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/normalizers.ts create mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_edit_policy_extension_wrapper.test.tsx delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension.tsx delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts delete mode 100644 x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.tsx diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/script_recorder_fields.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/script_recorder_fields.tsx index 65cbba050e8e..2bdf1e4c82eb 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/script_recorder_fields.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/script_recorder_fields.tsx @@ -63,7 +63,7 @@ export function ScriptRecorderFields({ onChange, script, fileName, isEditable }: iconSide="right" > @@ -78,7 +78,7 @@ export function ScriptRecorderFields({ onChange, script, fileName, isEditable }: color="danger" > diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.test.tsx index 884a81020ced..74ead8c6906d 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.test.tsx @@ -23,7 +23,7 @@ import { defaultBrowserAdvancedFields as defaultConfig, defaultBrowserSimpleFields, } from '../contexts'; -import { validate as centralValidation } from '../validation'; +import { validate as centralValidation } from '../../monitor_management/validation'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; const defaultValidation = centralValidation[DataStream.BROWSER]; @@ -89,29 +89,14 @@ describe('', () => { }); }); - it('only displayed filter options when zip url is truthy', () => { - const { queryByText, getByText, rerender } = render(); + it('does not display filter options (zip url has been deprecated)', () => { + const { queryByText } = render(); expect( queryByText( /Use these options to apply the selected monitor settings to a subset of the tests in your suite./ ) ).not.toBeInTheDocument(); - - rerender( - - ); - - expect( - getByText( - /Use these options to apply the selected monitor settings to a subset of the tests in your suite./ - ) - ).toBeInTheDocument(); }); it('renders upstream fields', () => { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.tsx index 64409608f7a4..8fe90c800f30 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.tsx @@ -7,18 +7,11 @@ import React, { memo, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiAccordion, - EuiSelect, - EuiFieldText, - EuiCheckbox, - EuiFormRow, - EuiSpacer, -} from '@elastic/eui'; +import { EuiAccordion, EuiSelect, EuiCheckbox, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { ComboBox } from '../combo_box'; import { DescribedFormGroupWithWrap } from '../common/described_form_group_with_wrap'; -import { useBrowserAdvancedFieldsContext, useBrowserSimpleFieldsContext } from '../contexts'; +import { useBrowserAdvancedFieldsContext } from '../contexts'; import { ConfigKey, Validation, ScreenshotOption } from '../types'; @@ -35,7 +28,6 @@ interface Props { export const BrowserAdvancedFields = memo( ({ validate, children, minColumnWidth, onFieldBlur }) => { const { fields, setFields } = useBrowserAdvancedFieldsContext(); - const { fields: simpleFields } = useBrowserSimpleFieldsContext(); const handleInputChange = useCallback( ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { @@ -51,78 +43,6 @@ export const BrowserAdvancedFields = memo( data-test-subj="syntheticsBrowserAdvancedFieldsAccordion" > - {simpleFields[ConfigKey.SOURCE_ZIP_URL] && ( - - - - } - description={ - - } - > - - - } - labelAppend={} - helpText={ - - } - > - - handleInputChange({ - value: event.target.value, - configKey: ConfigKey.JOURNEY_FILTERS_MATCH, - }) - } - onBlur={() => onFieldBlur?.(ConfigKey.JOURNEY_FILTERS_MATCH)} - data-test-subj="syntheticsBrowserJourneyFiltersMatch" - /> - - - } - labelAppend={} - helpText={ - - } - > - - handleInputChange({ value, configKey: ConfigKey.JOURNEY_FILTERS_TAGS }) - } - onBlur={() => onFieldBlur?.(ConfigKey.JOURNEY_FILTERS_TAGS)} - data-test-subj="syntheticsBrowserJourneyFiltersTags" - /> - - - )} { - const makeThrottlingConfig = (value: string) => ({ - [ConfigKey.THROTTLING_CONFIG]: { value }, - }); - - describe('throttlingToParameterNormalizer', () => { - it('can extract download values', () => { - const fields = makeThrottlingConfig('10d/5u/2.5l'); - - expect(getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED)(fields)).toEqual('10'); - }); - - it('can extract upload values', () => { - const fields = makeThrottlingConfig('10d/5u/2.5l'); - - expect(getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED)(fields)).toEqual('5'); - }); - - it('can extract latency values', () => { - const fields = makeThrottlingConfig('10d/5u/2.5l'); - - expect(getThrottlingParamNormalizer(ConfigKey.LATENCY)(fields)).toEqual('2.5'); - }); - - it('returns default values when throttling is disabled', () => { - const fields = makeThrottlingConfig('false'); - - expect(getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED)(fields)).toEqual( - defaultBrowserAdvancedFields[ConfigKey.DOWNLOAD_SPEED] - ); - expect(getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED)(fields)).toEqual( - defaultBrowserAdvancedFields[ConfigKey.UPLOAD_SPEED] - ); - expect(getThrottlingParamNormalizer(ConfigKey.LATENCY)(fields)).toEqual( - defaultBrowserAdvancedFields[ConfigKey.LATENCY] - ); - }); - - it("returns default values when the desired suffix doesn't exist", () => { - const noUploadFields = makeThrottlingConfig('10d/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED)(noUploadFields)).toEqual( - defaultBrowserAdvancedFields[ConfigKey.UPLOAD_SPEED] - ); - - const noDownloadFields = makeThrottlingConfig('10u/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED)(noDownloadFields)).toEqual( - defaultBrowserAdvancedFields[ConfigKey.DOWNLOAD_SPEED] - ); - - const noLatencyFields = makeThrottlingConfig('10d/5u'); - expect(getThrottlingParamNormalizer(ConfigKey.LATENCY)(noLatencyFields)).toEqual( - defaultBrowserAdvancedFields[ConfigKey.LATENCY] - ); - }); - }); - - describe('isThrottlingEnabledNormalizer', () => { - it('returns true for any value that is not "false"', () => { - expect(isThrottlingEnabledNormalizer(makeThrottlingConfig('10d/2l'))).toEqual(true); - expect(isThrottlingEnabledNormalizer(makeThrottlingConfig('test'))).toEqual(true); - }); - - it('returns false when throttling config is the string "false"', () => { - expect(isThrottlingEnabledNormalizer(makeThrottlingConfig('false'))).toEqual(false); - }); - }); -}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts deleted file mode 100644 index d1d9917f19c3..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - BrowserFields, - ConfigKey, - ThrottlingSuffix, - ThrottlingConfigKey, - configKeyToThrottlingSuffix, -} from '../types'; -import { - Normalizer, - commonNormalizers, - getNormalizer, - getJsonToJavascriptNormalizer, -} from '../common/normalizers'; -import { tlsNormalizers } from '../tls/normalizers'; - -import { defaultBrowserSimpleFields, defaultBrowserAdvancedFields } from '../contexts'; - -export type BrowserNormalizerMap = Record; - -const defaultBrowserFields = { - ...defaultBrowserSimpleFields, - ...defaultBrowserAdvancedFields, -}; - -export const getBrowserNormalizer = (key: ConfigKey) => { - return getNormalizer(key, defaultBrowserFields); -}; - -export const getBrowserJsonToJavascriptNormalizer = (key: ConfigKey) => { - return getJsonToJavascriptNormalizer(key, defaultBrowserFields); -}; - -export function throttlingToParameterNormalizer( - suffix: ThrottlingSuffix, - throttlingConfigValue?: string -): unknown { - if (!throttlingConfigValue || throttlingConfigValue === 'false') return null; - return ( - throttlingConfigValue - .split('/') - .filter((p) => p.endsWith(suffix))[0] - ?.slice(0, -1) ?? null - ); -} - -export const isThrottlingEnabledNormalizer: Normalizer = function isThrottlingEnabledNormalizer( - fields -) { - const throttlingEnabled = fields?.[ConfigKey.THROTTLING_CONFIG]?.value; - - // If we have any value that's not an explicit "false" it means throttling is "on" - return throttlingEnabled !== 'false'; -}; - -export function getThrottlingParamNormalizer(key: ThrottlingConfigKey): Normalizer { - const paramSuffix = configKeyToThrottlingSuffix[key]; - return (fields) => - throttlingToParameterNormalizer(paramSuffix, fields?.[ConfigKey.THROTTLING_CONFIG]?.value) ?? - defaultBrowserFields[key]; -} - -export const browserNormalizers: BrowserNormalizerMap = { - [ConfigKey.METADATA]: getBrowserJsonToJavascriptNormalizer(ConfigKey.METADATA), - [ConfigKey.URLS]: getBrowserNormalizer(ConfigKey.URLS), - [ConfigKey.PORT]: getBrowserNormalizer(ConfigKey.PORT), - [ConfigKey.SOURCE_ZIP_URL]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_URL), - [ConfigKey.SOURCE_ZIP_USERNAME]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_USERNAME), - [ConfigKey.SOURCE_ZIP_PASSWORD]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_PASSWORD), - [ConfigKey.SOURCE_ZIP_FOLDER]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_FOLDER), - [ConfigKey.SOURCE_PROJECT_CONTENT]: getBrowserNormalizer(ConfigKey.SOURCE_PROJECT_CONTENT), - [ConfigKey.SOURCE_INLINE]: getBrowserJsonToJavascriptNormalizer(ConfigKey.SOURCE_INLINE), - [ConfigKey.SOURCE_ZIP_PROXY_URL]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_PROXY_URL), - [ConfigKey.PARAMS]: getBrowserNormalizer(ConfigKey.PARAMS), - [ConfigKey.SCREENSHOTS]: getBrowserNormalizer(ConfigKey.SCREENSHOTS), - [ConfigKey.SYNTHETICS_ARGS]: getBrowserJsonToJavascriptNormalizer(ConfigKey.SYNTHETICS_ARGS), - [ConfigKey.IS_THROTTLING_ENABLED]: isThrottlingEnabledNormalizer, - [ConfigKey.DOWNLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED), - [ConfigKey.UPLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED), - [ConfigKey.LATENCY]: getThrottlingParamNormalizer(ConfigKey.LATENCY), - [ConfigKey.THROTTLING_CONFIG]: getBrowserNormalizer(ConfigKey.THROTTLING_CONFIG), - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: getBrowserJsonToJavascriptNormalizer( - ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES - ), - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: getBrowserJsonToJavascriptNormalizer( - ConfigKey.ZIP_URL_TLS_CERTIFICATE - ), - [ConfigKey.ZIP_URL_TLS_KEY]: getBrowserJsonToJavascriptNormalizer(ConfigKey.ZIP_URL_TLS_KEY), - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: getBrowserNormalizer( - ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE - ), - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: getBrowserNormalizer( - ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE - ), - [ConfigKey.ZIP_URL_TLS_VERSION]: getBrowserJsonToJavascriptNormalizer( - ConfigKey.ZIP_URL_TLS_VERSION - ), - [ConfigKey.JOURNEY_FILTERS_MATCH]: getBrowserJsonToJavascriptNormalizer( - ConfigKey.JOURNEY_FILTERS_MATCH - ), - [ConfigKey.JOURNEY_FILTERS_TAGS]: getBrowserJsonToJavascriptNormalizer( - ConfigKey.JOURNEY_FILTERS_TAGS - ), - [ConfigKey.IGNORE_HTTPS_ERRORS]: getBrowserNormalizer(ConfigKey.IGNORE_HTTPS_ERRORS), - [ConfigKey.PLAYWRIGHT_OPTIONS]: getBrowserNormalizer(ConfigKey.PLAYWRIGHT_OPTIONS), - [ConfigKey.TEXT_ASSERTION]: getBrowserNormalizer(ConfigKey.TEXT_ASSERTION), - ...commonNormalizers, - ...tlsNormalizers, -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.tsx index 9765e83e914e..7fb2f8382ae3 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.tsx @@ -50,7 +50,7 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { target="_blank" > @@ -76,7 +76,7 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { iconSide="right" > @@ -91,7 +91,7 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { color="danger" > diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/simple_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/simple_fields.tsx index 25ed39569bc8..5c8516d56ddf 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/simple_fields.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/simple_fields.tsx @@ -29,24 +29,9 @@ export const BrowserSimpleFields = memo(({ validate, onFieldBlur }) => { [setFields] ); const onChangeSourceField = useCallback( - ({ - zipUrl, - folder, - username, - password, - inlineScript, - params, - proxyUrl, - isGeneratedScript, - fileName, - }) => { + ({ inlineScript, params, isGeneratedScript, fileName }) => { setFields((prevFields) => ({ ...prevFields, - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_ZIP_PROXY_URL]: proxyUrl, - [ConfigKey.SOURCE_ZIP_FOLDER]: folder, - [ConfigKey.SOURCE_ZIP_USERNAME]: username, - [ConfigKey.SOURCE_ZIP_PASSWORD]: password, [ConfigKey.SOURCE_INLINE]: inlineScript, [ConfigKey.PARAMS]: params, [ConfigKey.METADATA]: { @@ -110,11 +95,6 @@ export const BrowserSimpleFields = memo(({ validate, onFieldBlur }) => { onFieldBlur={onFieldBlur} defaultConfig={useMemo( () => ({ - zipUrl: defaultValues[ConfigKey.SOURCE_ZIP_URL], - proxyUrl: defaultValues[ConfigKey.SOURCE_ZIP_PROXY_URL], - folder: defaultValues[ConfigKey.SOURCE_ZIP_FOLDER], - username: defaultValues[ConfigKey.SOURCE_ZIP_USERNAME], - password: defaultValues[ConfigKey.SOURCE_ZIP_PASSWORD], inlineScript: defaultValues[ConfigKey.SOURCE_INLINE], params: defaultValues[ConfigKey.PARAMS], isGeneratedScript: diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx index 34706d4d17cb..2199ac4b0296 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx @@ -7,11 +7,10 @@ import 'jest-canvas-mock'; import React from 'react'; -import { fireEvent, screen, waitFor } from '@testing-library/react'; -import { ConfigKey } from '../../../../../common/runtime_types'; +import { fireEvent, screen } from '@testing-library/react'; import { render } from '../../../lib/helper/rtl_helpers'; import { IPolicyConfigContextProvider } from '../contexts/policy_config_context'; -import { SourceField, Props, defaultValues } from './source_field'; +import { SourceField, Props } from './source_field'; import { BrowserSimpleFieldsContextProvider, PolicyConfigContextProvider } from '../contexts'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ @@ -31,14 +30,14 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { ...original, // Mocking CodeEditor, which uses React Monaco under the hood CodeEditor: (props: any) => ( - { - props.onChange(e.jsonContent); - }} - /> + <> + + ), }; }); @@ -48,11 +47,10 @@ const onBlur = jest.fn(); describe('', () => { const WrappedComponent = ({ - isZipUrlSourceEnabled, defaultConfig, }: Omit & Partial) => { return ( - + @@ -64,34 +62,6 @@ describe('', () => { jest.clearAllMocks(); }); - it('calls onChange', async () => { - render(); - const zipUrl = 'test.zip'; - - const zip = screen.getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - - const zipUrlField = screen.getByTestId('syntheticsBrowserZipUrl'); - fireEvent.change(zipUrlField, { target: { value: zipUrl } }); - - await waitFor(() => { - expect(onChange).toBeCalledWith({ ...defaultValues, zipUrl }); - }); - }); - - it('calls onBlur', () => { - render(); - - const zip = screen.getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - - const zipUrlField = screen.getByTestId('syntheticsBrowserZipUrl'); - fireEvent.click(zipUrlField); - fireEvent.blur(zipUrlField); - - expect(onBlur).toBeCalledWith(ConfigKey.SOURCE_ZIP_URL); - }); - it('selects inline script by default', () => { render(); @@ -100,15 +70,9 @@ describe('', () => { ).toBeInTheDocument(); }); - it('shows zip source type by default', async () => { + it('does not show ZipUrl source type', async () => { render(); - expect(screen.getByTestId('syntheticsSourceTab__zipUrl')).toBeInTheDocument(); - }); - - it('does not show ZipUrl source type when isZipUrlSourceEnabled = false', async () => { - render(); - expect(screen.queryByTestId('syntheticsSourceTab__zipUrl')).not.toBeInTheDocument(); }); @@ -124,19 +88,5 @@ describe('', () => { fireEvent.click(recorder); expect(getByText('Parameters')).toBeInTheDocument(); - - const zip = getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - - expect(getByText('Parameters')).toBeInTheDocument(); - }); - - it('shows deprecated for zip url', () => { - const { getByText, getByTestId } = render(); - - const zip = getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - - expect(getByText('Zip URL is deprecated')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.tsx index be3fe1a4d8d1..5a6cfef79bdd 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.tsx @@ -10,38 +10,24 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { EuiCode, - EuiCallOut, - EuiLink, EuiTabbedContent, - EuiTabbedContentTab, EuiFormRow, - EuiFieldText, - EuiFieldPassword, EuiSpacer, EuiBetaBadge, EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { usePolicyConfigContext } from '../contexts'; import { OptionalLabel } from '../optional_label'; import { CodeEditor } from '../code_editor'; import { ScriptRecorderFields } from './script_recorder_fields'; -import { ZipUrlTLSFields } from './zip_url_tls_fields'; import { ConfigKey, MonacoEditorLangId, Validation } from '../types'; -import { getDocLinks } from '../../../../kibana_services'; enum SourceType { INLINE = 'syntheticsBrowserInlineConfig', SCRIPT_RECORDER = 'syntheticsBrowserScriptRecorderConfig', - ZIP = 'syntheticsBrowserZipURLConfig', } interface SourceConfig { - zipUrl: string; - proxyUrl: string; - folder: string; - username: string; - password: string; inlineScript: string; params: string; isGeneratedScript?: boolean; @@ -56,11 +42,6 @@ export interface Props { } export const defaultValues = { - zipUrl: '', - proxyUrl: '', - folder: '', - username: '', - password: '', inlineScript: '', params: '', isGeneratedScript: false, @@ -81,7 +62,6 @@ export const SourceField = ({ defaultConfig = defaultValues, validate, }: Props) => { - const { isZipUrlSourceEnabled } = usePolicyConfigContext(); const [sourceType, setSourceType] = useState(getDefaultTab(defaultConfig)); const [config, setConfig] = useState(defaultConfig); @@ -94,18 +74,6 @@ export const SourceField = ({ [ConfigKey.SOURCE_INLINE]: config.inlineScript, }) ?? false; - const isZipUrlInvalid = - validate?.[ConfigKey.SOURCE_ZIP_URL]?.({ - [ConfigKey.SOURCE_ZIP_URL]: config.zipUrl, - }) ?? false; - - const zipUrlLabel = ( - - ); - const params = ( ); - const zipUrlSourceTabId = 'syntheticsBrowserZipURLConfig'; - const allTabs = [ + const tabs = [ { id: 'syntheticsBrowserInlineConfig', name: ( @@ -240,176 +207,8 @@ export const SourceField = ({ ), }, - { - id: zipUrlSourceTabId, - name: zipUrlLabel, - 'data-test-subj': `syntheticsSourceTab__zipUrl`, - content: ( - <> - - - } - size="s" - color="warning" - > - - - - ), - }} - /> - - - - } - helpText={ - - } - > - - setConfig((prevConfig) => ({ ...prevConfig, zipUrl: value })) - } - onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_URL)} - value={config.zipUrl} - data-test-subj="syntheticsBrowserZipUrl" - /> - - - - } - labelAppend={} - helpText={ - - } - > - - setConfig((prevConfig) => ({ ...prevConfig, proxyUrl: value })) - } - onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_PROXY_URL)} - value={config.proxyUrl} - data-test-subj="syntheticsBrowserZipUrlProxy" - /> - - - } - labelAppend={} - helpText={ - - } - > - - setConfig((prevConfig) => ({ ...prevConfig, folder: value })) - } - onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_FOLDER)} - value={config.folder} - data-test-subj="syntheticsBrowserZipUrlFolder" - /> - - {params} - - } - labelAppend={} - helpText={ - - } - > - - setConfig((prevConfig) => ({ ...prevConfig, username: value })) - } - onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_USERNAME)} - value={config.username} - data-test-subj="syntheticsBrowserZipUrlUsername" - /> - - - } - labelAppend={} - helpText={ - - } - > - - setConfig((prevConfig) => ({ ...prevConfig, password: value })) - } - onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_PASSWORD)} - value={config.password} - data-test-subj="syntheticsBrowserZipUrlPassword" - /> - - - ), - }, ]; - const tabs = isZipUrlSourceEnabled - ? allTabs - : allTabs.filter((tab: EuiTabbedContentTab) => tab.id !== zipUrlSourceTabId); - return ( ({ - ...jest.requireActual('@elastic/eui/lib/services/accessibility/html_id_generator'), - htmlIdGenerator: () => () => `id-${Math.random()}`, -})); - -describe('', () => { - const WrappedComponent = ({ isEnabled = true }: { isEnabled?: boolean }) => { - return ( - - - - - - ); - }; - it('renders ZipUrlTLSFields', () => { - const { getByLabelText, getByText } = render(); - - const toggle = getByText('Enable TLS configuration for Zip URL'); - - fireEvent.click(toggle); - - expect(getByText('Certificate settings')).toBeInTheDocument(); - expect(getByText('Supported TLS protocols')).toBeInTheDocument(); - expect(getByLabelText('Client certificate')).toBeInTheDocument(); - expect(getByLabelText('Client key')).toBeInTheDocument(); - expect(getByLabelText('Certificate authorities')).toBeInTheDocument(); - expect(getByLabelText('Verification mode')).toBeInTheDocument(); - }); - - it('updates fields', async () => { - const { getByLabelText } = render(); - - const clientCertificate = getByLabelText('Client certificate') as HTMLInputElement; - const clientKey = getByLabelText('Client key') as HTMLInputElement; - const clientKeyPassphrase = getByLabelText('Client key passphrase') as HTMLInputElement; - const certificateAuthorities = getByLabelText('Certificate authorities') as HTMLInputElement; - const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; - - const newValues = { - [ConfigKey.TLS_CERTIFICATE]: 'sampleClientCertificate', - [ConfigKey.TLS_KEY]: 'sampleClientKey', - [ConfigKey.TLS_KEY_PASSPHRASE]: 'sampleClientKeyPassphrase', - [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 'sampleCertificateAuthorities', - [ConfigKey.TLS_VERIFICATION_MODE]: VerificationMode.NONE, - }; - - fireEvent.change(clientCertificate, { - target: { value: newValues[ConfigKey.TLS_CERTIFICATE] }, - }); - fireEvent.change(clientKey, { target: { value: newValues[ConfigKey.TLS_KEY] } }); - fireEvent.change(clientKeyPassphrase, { - target: { value: newValues[ConfigKey.TLS_KEY_PASSPHRASE] }, - }); - fireEvent.change(certificateAuthorities, { - target: { value: newValues[ConfigKey.TLS_CERTIFICATE_AUTHORITIES] }, - }); - fireEvent.change(verificationMode, { - target: { value: newValues[ConfigKey.TLS_VERIFICATION_MODE] }, - }); - - expect(clientCertificate.value).toEqual(newValues[ConfigKey.TLS_CERTIFICATE]); - expect(clientKey.value).toEqual(newValues[ConfigKey.TLS_KEY]); - expect(certificateAuthorities.value).toEqual(newValues[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]); - expect(verificationMode.value).toEqual(newValues[ConfigKey.TLS_VERIFICATION_MODE]); - }); - - it('shows warning when verification mode is set to none', () => { - const { getByLabelText, getByText } = render(); - - const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; - - fireEvent.change(verificationMode, { - target: { value: VerificationMode.NONE }, - }); - - expect(getByText('Disabling TLS')).toBeInTheDocument(); - }); - - it('does not show fields when isEnabled is false', async () => { - const { queryByLabelText } = render(); - - expect(queryByLabelText('Client certificate')).not.toBeInTheDocument(); - expect(queryByLabelText('Client key')).not.toBeInTheDocument(); - expect(queryByLabelText('Client key passphrase')).not.toBeInTheDocument(); - expect(queryByLabelText('Certificate authorities')).not.toBeInTheDocument(); - expect(queryByLabelText('verification mode')).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.tsx deleted file mode 100644 index 21f2a548413c..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useEffect } from 'react'; - -import { EuiSwitch, EuiFormRow } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n-react'; - -import { TLSOptions, TLSConfig } from '../common/tls_options'; -import { - useBrowserSimpleFieldsContext, - usePolicyConfigContext, - defaultTLSFields, -} from '../contexts'; - -import { ConfigKey } from '../types'; - -export const ZipUrlTLSFields = () => { - const { defaultValues, setFields } = useBrowserSimpleFieldsContext(); - const { isZipUrlTLSEnabled, setIsZipUrlTLSEnabled } = usePolicyConfigContext(); - - const handleOnChange = useCallback( - (tlsConfig: TLSConfig) => { - setFields((prevFields) => ({ - ...prevFields, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: tlsConfig.certificateAuthorities, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: tlsConfig.certificate, - [ConfigKey.ZIP_URL_TLS_KEY]: tlsConfig.key, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: tlsConfig.keyPassphrase, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: tlsConfig.verificationMode, - [ConfigKey.ZIP_URL_TLS_VERSION]: tlsConfig.version, - })); - }, - [setFields] - ); - - useEffect(() => { - if (!isZipUrlTLSEnabled) { - setFields((prevFields) => ({ - ...prevFields, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: undefined, - [ConfigKey.ZIP_URL_TLS_KEY]: undefined, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, - [ConfigKey.ZIP_URL_TLS_VERSION]: undefined, - })); - } - }, [setFields, isZipUrlTLSEnabled]); - - return ( - - <> - - } - onChange={(event) => setIsZipUrlTLSEnabled(event.target.checked)} - /> - {isZipUrlTLSEnabled ? ( - - ) : null} - - - ); -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx index d089dacfaedb..cf91e60c6da2 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx @@ -23,16 +23,12 @@ interface IPolicyConfigContext { setName: React.Dispatch>; setLocations: React.Dispatch>; setIsTLSEnabled: React.Dispatch>; - setIsZipUrlTLSEnabled: React.Dispatch>; setNamespace: React.Dispatch>; monitorType: DataStream; defaultMonitorType: DataStream; isTLSEnabled?: boolean; - isZipUrlTLSEnabled?: boolean; - isZipUrlSourceEnabled?: boolean; runsOnService?: boolean; defaultIsTLSEnabled?: boolean; - defaultIsZipUrlTLSEnabled?: boolean; isEditable?: boolean; defaultName?: string; name?: string; @@ -50,12 +46,10 @@ export interface IPolicyConfigContextProvider { defaultMonitorType?: DataStream; runsOnService?: boolean; defaultIsTLSEnabled?: boolean; - defaultIsZipUrlTLSEnabled?: boolean; defaultName?: string; defaultLocations?: MonitorServiceLocations; defaultNamespace?: string; isEditable?: boolean; - isZipUrlSourceEnabled?: boolean; allowedScheduleUnits?: ScheduleUnit[]; throttling?: ThrottlingOptions; sourceType?: SourceType; @@ -76,11 +70,6 @@ export const defaultContext: IPolicyConfigContext = { setIsTLSEnabled: (_isTLSEnabled: React.SetStateAction) => { throw new Error('setIsTLSEnabled was not initialized, set it when you invoke the context'); }, - setIsZipUrlTLSEnabled: (_isZipUrlTLSEnabled: React.SetStateAction) => { - throw new Error( - 'setIsZipUrlTLSEnabled was not initialized, set it when you invoke the context' - ); - }, setNamespace: (_namespace: React.SetStateAction) => { throw new Error('setNamespace was not initialized, set it when you invoke the context'); }, @@ -88,11 +77,9 @@ export const defaultContext: IPolicyConfigContext = { defaultMonitorType: initialMonitorTypeValue, // immutable, runsOnService: false, defaultIsTLSEnabled: false, - defaultIsZipUrlTLSEnabled: false, defaultName: '', defaultLocations: [], isEditable: false, - isZipUrlSourceEnabled: true, allowedScheduleUnits: [ScheduleUnit.MINUTES, ScheduleUnit.SECONDS], defaultNamespace: DEFAULT_NAMESPACE_STRING, throttling: DEFAULT_THROTTLING, @@ -106,13 +93,11 @@ export function PolicyConfigContextProvider({ throttling = DEFAULT_THROTTLING, defaultMonitorType = initialMonitorTypeValue, defaultIsTLSEnabled = false, - defaultIsZipUrlTLSEnabled = false, defaultName = '', defaultLocations = [], defaultNamespace = DEFAULT_NAMESPACE_STRING, isEditable = false, runsOnService = false, - isZipUrlSourceEnabled = true, allowedScheduleUnits = [ScheduleUnit.MINUTES, ScheduleUnit.SECONDS], sourceType, }: IPolicyConfigContextProvider) { @@ -120,7 +105,6 @@ export function PolicyConfigContextProvider({ const [name, setName] = useState(defaultName); const [locations, setLocations] = useState(defaultLocations); const [isTLSEnabled, setIsTLSEnabled] = useState(defaultIsTLSEnabled); - const [isZipUrlTLSEnabled, setIsZipUrlTLSEnabled] = useState(defaultIsZipUrlTLSEnabled); const [namespace, setNamespace] = useState(defaultNamespace); const isAddMonitorRoute = useRouteMatch(MONITOR_ADD_ROUTE); @@ -138,11 +122,8 @@ export function PolicyConfigContextProvider({ defaultMonitorType, runsOnService, isTLSEnabled, - isZipUrlTLSEnabled, setIsTLSEnabled, - setIsZipUrlTLSEnabled, defaultIsTLSEnabled, - defaultIsZipUrlTLSEnabled, isEditable, defaultName, name, @@ -150,7 +131,6 @@ export function PolicyConfigContextProvider({ defaultLocations, locations, setLocations, - isZipUrlSourceEnabled, allowedScheduleUnits, namespace, setNamespace, @@ -162,10 +142,7 @@ export function PolicyConfigContextProvider({ defaultMonitorType, runsOnService, isTLSEnabled, - isZipUrlSourceEnabled, - isZipUrlTLSEnabled, defaultIsTLSEnabled, - defaultIsZipUrlTLSEnabled, isEditable, name, defaultName, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.test.tsx index 00f6ca347bf0..6531024bea28 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.test.tsx @@ -19,7 +19,7 @@ import { } from './contexts'; import { CustomFields } from './custom_fields'; import { ConfigKey, DataStream, ScheduleUnit } from './types'; -import { validate as centralValidation } from './validation'; +import { validate as centralValidation } from '../monitor_management/validation'; import { defaultConfig } from './synthetics_policy_create_extension'; // ensures that fields appropriately match to their label @@ -201,7 +201,7 @@ describe('', () => { }); it('handles switching monitor type', () => { - const { getByText, queryByText, getByLabelText, queryByLabelText, getAllByLabelText } = render( + const { getByText, queryByText, getByLabelText, queryByLabelText } = render( ); const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; @@ -251,20 +251,10 @@ describe('', () => { screen.getByText('Runs Synthetic test scripts that are defined inline.') ).toBeInTheDocument(); - const zip = screen.getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - - getAllByLabelText('Zip URL').forEach((node: any) => { - expect(node).toBeInTheDocument(); - }); expect( getByText(/To create a "Browser" monitor, please ensure you are using the/) ).toBeInTheDocument(); - // expect tls options to be available for browser - expect(queryByLabelText('Proxy Zip URL')).toBeInTheDocument(); - expect(queryByText(/Enable TLS configuration for Zip URL/)).toBeInTheDocument(); - // ensure at least one browser advanced option is present advancedOptionsButton = getByText('Advanced Browser options'); fireEvent.click(advancedOptionsButton); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/normalizers.ts deleted file mode 100644 index 60aa607aebe6..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/normalizers.ts +++ /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 { DataStream } from '../types'; - -import { httpNormalizers, HTTPNormalizerMap } from '../http/normalizers'; -import { tcpNormalizers, TCPNormalizerMap } from '../tcp/normalizers'; -import { icmpNormalizers, ICMPNormalizerMap } from '../icmp/normalizers'; -import { browserNormalizers, BrowserNormalizerMap } from '../browser/normalizers'; -import { commonNormalizers, CommonNormalizerMap } from '../common/normalizers'; - -type Normalizers = HTTPNormalizerMap & - ICMPNormalizerMap & - TCPNormalizerMap & - BrowserNormalizerMap & - CommonNormalizerMap; - -interface NormalizerMap { - [DataStream.HTTP]: HTTPNormalizerMap; - [DataStream.ICMP]: ICMPNormalizerMap; - [DataStream.TCP]: TCPNormalizerMap; - [DataStream.BROWSER]: BrowserNormalizerMap; -} - -export const normalizersMap: NormalizerMap = { - [DataStream.HTTP]: httpNormalizers, - [DataStream.ICMP]: icmpNormalizers, - [DataStream.TCP]: tcpNormalizers, - [DataStream.BROWSER]: browserNormalizers, -}; - -export const normalizers: Normalizers = { - ...httpNormalizers, - ...icmpNormalizers, - ...tcpNormalizers, - ...browserNormalizers, - ...commonNormalizers, -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts index 9f43cbb238a7..e0c7eb860f21 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts @@ -33,7 +33,6 @@ export const defaultConfig: PolicyConfig = DEFAULT_FIELDS; export const usePolicy = (fleetPolicyName: string = '') => { const { isTLSEnabled, - isZipUrlTLSEnabled, name: monitorName, // the monitor name can come from two different places, either from fleet or from uptime locations, namespace, @@ -50,9 +49,8 @@ export const usePolicy = (fleetPolicyName: string = '') => { const metadata = useMemo( () => ({ is_tls_enabled: isTLSEnabled, - is_zip_url_tls_enabled: isZipUrlTLSEnabled, }), - [isTLSEnabled, isZipUrlTLSEnabled] + [isTLSEnabled] ); /* TODO add locations to policy config for synthetics service */ diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.test.tsx deleted file mode 100644 index f285c70ea57e..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.test.tsx +++ /dev/null @@ -1,680 +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 { renderHook } from '@testing-library/react-hooks'; -import { useUpdatePolicy } from './use_update_policy'; -import { NewPackagePolicy } from '@kbn/fleet-plugin/public'; -import { validate } from '../validation'; -import { - ConfigKey, - MonitorFields, - DataStream, - TLSVersion, - CommonFields, - ScheduleUnit, - ICMPFields, - TCPFields, - TLSFields, - HTTPFields, - BrowserFields, -} from '../types'; -import { defaultConfig } from '../synthetics_policy_create_extension'; - -describe('useUpdatePolicy', () => { - const newPolicy: NewPackagePolicy = { - name: '', - description: '', - namespace: 'default', - policy_id: 'ae774160-8e49-11eb-aba5-99269d21ba6e', - enabled: true, - inputs: [ - { - type: 'synthetics/http', - enabled: true, - streams: [ - { - enabled: true, - data_stream: { - type: 'synthetics', - dataset: 'http', - }, - vars: { - type: { - value: 'http', - type: 'text', - }, - name: { - value: '', - type: 'text', - }, - schedule: { - value: '"@every 3m"', - type: 'text', - }, - urls: { - value: '', - type: 'text', - }, - 'service.name': { - value: '', - type: 'text', - }, - timeout: { - value: '16s', - type: 'text', - }, - max_redirects: { - value: 0, - type: 'integer', - }, - proxy_url: { - value: '', - type: 'text', - }, - tags: { - value: '[]', - type: 'yaml', - }, - 'response.include_headers': { - value: true, - type: 'bool', - }, - 'response.include_body': { - value: 'on_error', - type: 'text', - }, - 'check.request.method': { - value: 'GET', - type: 'text', - }, - 'check.request.headers': { - value: '{}', - type: 'yaml', - }, - 'check.request.body': { - value: '""', - type: 'yaml', - }, - 'check.response.status': { - value: '[]', - type: 'yaml', - }, - 'check.response.headers': { - value: '{}', - type: 'yaml', - }, - 'check.response.body.positive': { - value: null, - type: 'yaml', - }, - 'check.response.body.negative': { - value: null, - type: 'yaml', - }, - 'ssl.certificate_authorities': { - value: '', - type: 'yaml', - }, - 'ssl.certificate': { - value: '', - type: 'yaml', - }, - 'ssl.key': { - value: '', - type: 'yaml', - }, - 'ssl.key_passphrase': { - type: 'text', - }, - 'ssl.verification_mode': { - value: 'full', - type: 'text', - }, - 'ssl.supported_protocols': { - value: '', - type: 'yaml', - }, - }, - }, - ], - }, - { - type: 'synthetics/tcp', - enabled: false, - streams: [ - { - enabled: false, - data_stream: { - type: 'synthetics', - dataset: 'tcp', - }, - vars: { - type: { - value: 'tcp', - type: 'text', - }, - name: { - type: 'text', - }, - schedule: { - value: '10s', - type: 'text', - }, - hosts: { - type: 'text', - }, - 'service.name': { - type: 'text', - }, - timeout: { - type: 'integer', - }, - max_redirects: { - type: 'integer', - }, - proxy_url: { - type: 'text', - }, - proxy_use_local_resolver: { - value: false, - type: 'bool', - }, - tags: { - type: 'yaml', - }, - 'check.send': { - type: 'text', - }, - 'check.receive': { - type: 'yaml', - }, - 'ssl.certificate_authorities': { - type: 'yaml', - }, - 'ssl.certificate': { - type: 'yaml', - }, - 'ssl.key': { - type: 'yaml', - }, - 'ssl.key_passphrase': { - type: 'text', - }, - 'ssl.verification_mode': { - type: 'text', - }, - }, - }, - ], - }, - { - type: 'synthetics/icmp', - enabled: false, - streams: [ - { - enabled: false, - data_stream: { - type: 'synthetics', - dataset: 'icmp', - }, - vars: { - type: { - value: 'icmp', - type: 'text', - }, - name: { - type: 'text', - }, - schedule: { - value: '10s', - type: 'text', - }, - wait: { - value: '1s', - type: 'text', - }, - hosts: { - type: 'text', - }, - 'service.name': { - type: 'text', - }, - timeout: { - type: 'integer', - }, - max_redirects: { - type: 'integer', - }, - tags: { - type: 'yaml', - }, - }, - }, - ], - }, - { - type: 'synthetics/browser', - enabled: false, - streams: [ - { - enabled: false, - data_stream: { - type: 'synthetics', - dataset: 'browser', - }, - vars: { - type: { - value: 'browser', - type: 'text', - }, - name: { - value: 'Sample name', - type: 'text', - }, - schedule: { - value: '10s', - type: 'text', - }, - 'source.zip_url.url': { - type: 'text', - }, - 'source.zip_url.username': { - type: 'text', - }, - 'source.zip_url.password': { - type: 'password', - }, - 'source.zip_url.folder': { - type: 'text', - }, - 'source.inline.script': { - type: 'yaml', - }, - 'service.name': { - type: 'text', - }, - screenshots: { - type: 'text', - }, - synthetics_args: { - type: 'yaml', - }, - timeout: { - type: 'text', - }, - tags: { - type: 'yaml', - }, - 'throttling.download_speed': { - type: 'text', - value: '""', - }, - 'throttling.upload_speed': { - type: 'text', - value: '""', - }, - 'throttling.latency': { - type: 'text', - value: '""', - }, - 'throttling.config': { - type: 'text', - value: '""', - }, - }, - }, - ], - }, - ], - package: { - name: 'synthetics', - title: 'Elastic Synthetics', - version: '0.66.0', - }, - }; - - const defaultCommonFields: Partial = { - [ConfigKey.APM_SERVICE_NAME]: 'APM Service name', - [ConfigKey.TAGS]: ['some', 'tags'], - [ConfigKey.SCHEDULE]: { - number: '5', - unit: ScheduleUnit.MINUTES, - }, - [ConfigKey.TIMEOUT]: '17', - }; - - const defaultTLSFields: Partial = { - [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 'ca', - [ConfigKey.TLS_CERTIFICATE]: 'cert', - [ConfigKey.TLS_KEY]: 'key', - [ConfigKey.TLS_KEY_PASSPHRASE]: 'password', - }; - - it('handles http data stream', async () => { - const onChange = jest.fn(); - const initialProps = { - defaultConfig: defaultConfig[DataStream.HTTP], - config: defaultConfig[DataStream.HTTP], - newPolicy, - onChange, - validate, - monitorType: DataStream.HTTP, - }; - const { result, rerender, waitFor } = renderHook((props) => useUpdatePolicy(props), { - initialProps, - }); - - expect(result.current.config).toMatchObject({ ...defaultConfig[DataStream.HTTP] }); - - const config: HTTPFields = { - ...defaultConfig[DataStream.HTTP], - ...defaultCommonFields, - ...defaultTLSFields, - [ConfigKey.URLS]: 'url', - [ConfigKey.PROXY_URL]: 'proxyUrl', - }; - - // expect only http to be enabled - expect(result.current.updatedPolicy.inputs[0].enabled).toBe(true); - expect(result.current.updatedPolicy.inputs[1].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[2].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[3].enabled).toBe(false); - - rerender({ - ...initialProps, - config, - }); - - await waitFor(() => { - const vars = result.current.updatedPolicy.inputs[0]?.streams[0]?.vars; - - expect(vars?.[ConfigKey.MONITOR_TYPE].value).toEqual(config[ConfigKey.MONITOR_TYPE]); - expect(vars?.[ConfigKey.URLS].value).toEqual(config[ConfigKey.URLS]); - expect(vars?.[ConfigKey.SCHEDULE].value).toEqual( - JSON.stringify( - `@every ${config[ConfigKey.SCHEDULE].number}${config[ConfigKey.SCHEDULE].unit}` - ) - ); - expect(vars?.[ConfigKey.PROXY_URL].value).toEqual(config[ConfigKey.PROXY_URL]); - expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); - expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); - expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual(null); - expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual(null); - expect(vars?.[ConfigKey.RESPONSE_STATUS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKey.REQUEST_HEADERS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKey.RESPONSE_HEADERS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKey.RESPONSE_BODY_INDEX].value).toEqual( - config[ConfigKey.RESPONSE_BODY_INDEX] - ); - expect(vars?.[ConfigKey.RESPONSE_HEADERS_INDEX].value).toEqual( - config[ConfigKey.RESPONSE_HEADERS_INDEX] - ); - }); - }); - - it('stringifies array values and returns null for empty array values', async () => { - const onChange = jest.fn(); - const initialProps = { - defaultConfig: defaultConfig[DataStream.HTTP], - config: defaultConfig[DataStream.HTTP], - newPolicy, - onChange, - validate, - monitorType: DataStream.HTTP, - }; - const { rerender, result, waitFor } = renderHook((props) => useUpdatePolicy(props), { - initialProps, - }); - - rerender({ - ...initialProps, - config: { - ...defaultConfig[DataStream.HTTP], - [ConfigKey.METADATA]: { - is_tls_enabled: true, - }, - [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: ['test'], - [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: ['test'], - [ConfigKey.RESPONSE_STATUS_CHECK]: ['test'], - [ConfigKey.TAGS]: ['test'], - [ConfigKey.TLS_VERSION]: [TLSVersion.ONE_ONE], - }, - }); - - await waitFor(() => { - // expect only http to be enabled - expect(result.current.updatedPolicy.inputs[0].enabled).toBe(true); - expect(result.current.updatedPolicy.inputs[1].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[2].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[3].enabled).toBe(false); - - const vars = result.current.updatedPolicy.inputs[0]?.streams[0]?.vars; - - expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual('["test"]'); - expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual('["test"]'); - expect(vars?.[ConfigKey.RESPONSE_STATUS_CHECK].value).toEqual('["test"]'); - expect(vars?.[ConfigKey.TAGS].value).toEqual('["test"]'); - expect(vars?.[ConfigKey.TLS_VERSION].value).toEqual('["TLSv1.1"]'); - }); - - rerender({ - ...initialProps, - config: { - ...defaultConfig[DataStream.HTTP], - [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: [], - [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: [], - [ConfigKey.RESPONSE_STATUS_CHECK]: [], - [ConfigKey.TAGS]: [], - [ConfigKey.TLS_VERSION]: [], - }, - }); - - await waitFor(() => { - const vars = result.current.updatedPolicy.inputs[0]?.streams[0]?.vars; - - expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual(null); - expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual(null); - expect(vars?.[ConfigKey.RESPONSE_STATUS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKey.TAGS].value).toEqual(null); - expect(vars?.[ConfigKey.TLS_VERSION].value).toEqual(null); - }); - }); - - it('handles tcp data stream', async () => { - const onChange = jest.fn(); - const initialProps = { - defaultConfig: defaultConfig[DataStream.TCP], - config: defaultConfig[DataStream.TCP], - newPolicy, - onChange, - validate, - monitorType: DataStream.TCP, - }; - const { result, rerender, waitFor } = renderHook((props) => useUpdatePolicy(props), { - initialProps, - }); - - // expect only tcp to be enabled - expect(result.current.updatedPolicy.inputs[0].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[1].enabled).toBe(true); - expect(result.current.updatedPolicy.inputs[2].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[3].enabled).toBe(false); - - const config: TCPFields = { - ...defaultConfig[DataStream.TCP], - ...defaultCommonFields, - ...defaultTLSFields, - [ConfigKey.HOSTS]: 'sampleHost', - [ConfigKey.PROXY_URL]: 'proxyUrl', - [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: true, - [ConfigKey.RESPONSE_RECEIVE_CHECK]: 'response', - [ConfigKey.REQUEST_SEND_CHECK]: 'request', - }; - - rerender({ - ...initialProps, - config, - }); - - await waitFor(() => { - const vars = result.current.updatedPolicy.inputs[1]?.streams[0]?.vars; - - expect(onChange).toBeCalledWith({ - isValid: false, - updatedPolicy: result.current.updatedPolicy, - }); - - expect(vars?.[ConfigKey.MONITOR_TYPE].value).toEqual(config[ConfigKey.MONITOR_TYPE]); - expect(vars?.[ConfigKey.HOSTS].value).toEqual(config[ConfigKey.HOSTS]); - expect(vars?.[ConfigKey.SCHEDULE].value).toEqual( - JSON.stringify( - `@every ${config[ConfigKey.SCHEDULE].number}${config[ConfigKey.SCHEDULE].unit}` - ) - ); - expect(vars?.[ConfigKey.PROXY_URL].value).toEqual(config[ConfigKey.PROXY_URL]); - expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); - expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); - expect(vars?.[ConfigKey.PROXY_USE_LOCAL_RESOLVER].value).toEqual( - config[ConfigKey.PROXY_USE_LOCAL_RESOLVER] - ); - expect(vars?.[ConfigKey.RESPONSE_RECEIVE_CHECK].value).toEqual( - config[ConfigKey.RESPONSE_RECEIVE_CHECK] - ); - expect(vars?.[ConfigKey.REQUEST_SEND_CHECK].value).toEqual( - config[ConfigKey.REQUEST_SEND_CHECK] - ); - }); - }); - - it('handles icmp data stream', async () => { - const onChange = jest.fn(); - const initialProps = { - defaultConfig: defaultConfig[DataStream.ICMP], - config: defaultConfig[DataStream.ICMP], - newPolicy, - onChange, - validate, - monitorType: DataStream.ICMP, - }; - const { rerender, result, waitFor } = renderHook((props) => useUpdatePolicy(props), { - initialProps, - }); - const config: ICMPFields = { - ...defaultConfig[DataStream.ICMP], - ...defaultCommonFields, - [ConfigKey.WAIT]: '2', - [ConfigKey.HOSTS]: 'sampleHost', - }; - - // expect only icmp to be enabled - expect(result.current.updatedPolicy.inputs[0].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[1].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[2].enabled).toBe(true); - expect(result.current.updatedPolicy.inputs[3].enabled).toBe(false); - - // only call onChange when the policy is changed - rerender({ - ...initialProps, - config, - }); - - await waitFor(() => { - const vars = result.current.updatedPolicy.inputs[2]?.streams[0]?.vars; - - expect(vars?.[ConfigKey.MONITOR_TYPE].value).toEqual(config[ConfigKey.MONITOR_TYPE]); - expect(vars?.[ConfigKey.HOSTS].value).toEqual(config[ConfigKey.HOSTS]); - expect(vars?.[ConfigKey.SCHEDULE].value).toEqual( - JSON.stringify( - `@every ${config[ConfigKey.SCHEDULE].number}${config[ConfigKey.SCHEDULE].unit}` - ) - ); - expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); - expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); - expect(vars?.[ConfigKey.WAIT].value).toEqual(`${config[ConfigKey.WAIT]}s`); - - expect(onChange).toBeCalledWith({ - isValid: false, - updatedPolicy: result.current.updatedPolicy, - }); - }); - }); - - it('handles browser data stream', async () => { - const onChange = jest.fn(); - const initialProps = { - defaultConfig: defaultConfig[DataStream.BROWSER] as Partial, - config: defaultConfig[DataStream.BROWSER] as Partial, - newPolicy, - onChange, - validate, - monitorType: DataStream.BROWSER, - }; - - const { result, rerender, waitFor } = renderHook((props) => useUpdatePolicy(props), { - initialProps, - }); - - // expect only browser to be enabled - expect(result.current.updatedPolicy.inputs[0].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[1].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[2].enabled).toBe(false); - expect(result.current.updatedPolicy.inputs[3].enabled).toBe(true); - - const config: BrowserFields = { - ...defaultConfig[DataStream.BROWSER], - ...defaultCommonFields, - [ConfigKey.SOURCE_INLINE]: 'inlineScript', - [ConfigKey.SOURCE_ZIP_URL]: 'zipFolder', - [ConfigKey.SOURCE_ZIP_FOLDER]: 'zipFolder', - [ConfigKey.SOURCE_ZIP_USERNAME]: 'username', - [ConfigKey.SOURCE_ZIP_PASSWORD]: 'password', - [ConfigKey.SCREENSHOTS]: 'off', - [ConfigKey.SYNTHETICS_ARGS]: ['args'], - [ConfigKey.DOWNLOAD_SPEED]: '13', - [ConfigKey.UPLOAD_SPEED]: '3', - [ConfigKey.LATENCY]: '7', - }; - - rerender({ - ...initialProps, - config: config as Partial, - }); - - await waitFor(() => { - const vars = result.current.updatedPolicy.inputs[3]?.streams[0]?.vars; - - expect(vars?.[ConfigKey.SOURCE_ZIP_FOLDER].value).toEqual( - config[ConfigKey.SOURCE_ZIP_FOLDER] - ); - expect(vars?.[ConfigKey.SOURCE_ZIP_PASSWORD].value).toEqual( - config[ConfigKey.SOURCE_ZIP_PASSWORD] - ); - expect(vars?.[ConfigKey.SOURCE_ZIP_URL].value).toEqual(config[ConfigKey.SOURCE_ZIP_URL]); - expect(vars?.[ConfigKey.SOURCE_INLINE].value).toEqual( - JSON.stringify(config[ConfigKey.SOURCE_INLINE]) - ); - expect(vars?.[ConfigKey.SOURCE_ZIP_PASSWORD].value).toEqual( - config[ConfigKey.SOURCE_ZIP_PASSWORD] - ); - expect(vars?.[ConfigKey.SCREENSHOTS].value).toEqual(config[ConfigKey.SCREENSHOTS]); - expect(vars?.[ConfigKey.SYNTHETICS_ARGS].value).toEqual( - JSON.stringify(config[ConfigKey.SYNTHETICS_ARGS]) - ); - expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); - expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); - expect(vars?.[ConfigKey.THROTTLING_CONFIG].value).toEqual( - `${config[ConfigKey.DOWNLOAD_SPEED]}d/${config[ConfigKey.UPLOAD_SPEED]}u/${ - config[ConfigKey.LATENCY] - }l` - ); - - expect(onChange).toBeCalledWith({ - isValid: false, - updatedPolicy: result.current.updatedPolicy, - }); - }); - }); -}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts deleted file mode 100644 index 199b81179016..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { useEffect, useRef, useState } from 'react'; -import { NewPackagePolicy } from '@kbn/fleet-plugin/public'; -import { formatSyntheticsPolicy } from '../../../../../common/formatters/format_synthetics_policy'; -import { ConfigKey, DataStream, Validation, MonitorFields } from '../types'; - -interface Props { - monitorType: DataStream; - defaultConfig: Partial; - config: Partial; - newPolicy: NewPackagePolicy; - onChange: (opts: { - /** is current form state is valid */ - isValid: boolean; - /** The updated Integration Policy to be merged back and included in the API call */ - updatedPolicy: NewPackagePolicy; - }) => void; - validate: Record; -} - -export const useUpdatePolicy = ({ - monitorType, - defaultConfig, - config, - newPolicy, - onChange, - validate, -}: Props) => { - const [updatedPolicy, setUpdatedPolicy] = useState(newPolicy); - // Update the integration policy with our custom fields - const currentConfig = useRef>(defaultConfig); - - useEffect(() => { - const configKeys = Object.keys(config) as ConfigKey[]; - const validationKeys = Object.keys(validate[monitorType]) as ConfigKey[]; - const configDidUpdate = configKeys.some((key) => config[key] !== currentConfig.current[key]); - const isValid = - !!newPolicy.name && !validationKeys.find((key) => validate[monitorType]?.[key]?.(config)); - - const { formattedPolicy, dataStream, currentInput } = formatSyntheticsPolicy( - newPolicy, - monitorType, - config, - true - ); - - // prevent an infinite loop of updating the policy - if (currentInput && dataStream && configDidUpdate) { - currentConfig.current = config; - setUpdatedPolicy(formattedPolicy); - onChange({ - isValid, - updatedPolicy: formattedPolicy, - }); - } - }, [config, currentConfig, newPolicy, onChange, validate, monitorType]); - - return { - config, - updatedPolicy, - }; -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.test.tsx index c6d2d9046759..18937d5dc512 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.test.tsx @@ -19,7 +19,7 @@ import { HTTPMethod, Validation, } from '../types'; -import { validate as centralValidation } from '../validation'; +import { validate as centralValidation } from '../../monitor_management/validation'; import { HTTPAdvancedFields } from './advanced_fields'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts deleted file mode 100644 index a783639f1ab1..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts +++ /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 { parseJsonIfString } from '../helpers/parsers'; -import { HTTPFields, ConfigKey, ContentType, contentTypesToMode } from '../types'; -import { - Normalizer, - commonNormalizers, - getNormalizer, - getJsonToJavascriptNormalizer, -} from '../common/normalizers'; -import { tlsNormalizers } from '../tls/normalizers'; -import { defaultHTTPSimpleFields, defaultHTTPAdvancedFields } from '../contexts'; - -export type HTTPNormalizerMap = Record; - -const defaultHTTPValues = { - ...defaultHTTPSimpleFields, - ...defaultHTTPAdvancedFields, -}; - -export const getHTTPNormalizer = (key: ConfigKey) => { - return getNormalizer(key, defaultHTTPValues); -}; - -export const getHTTPJsonToJavascriptNormalizer = (key: ConfigKey) => { - return getJsonToJavascriptNormalizer(key, defaultHTTPValues); -}; - -export const httpNormalizers: HTTPNormalizerMap = { - [ConfigKey.METADATA]: getHTTPJsonToJavascriptNormalizer(ConfigKey.METADATA), - [ConfigKey.URLS]: getHTTPNormalizer(ConfigKey.URLS), - [ConfigKey.PORT]: getHTTPNormalizer(ConfigKey.PORT), - [ConfigKey.MAX_REDIRECTS]: getHTTPNormalizer(ConfigKey.MAX_REDIRECTS), - [ConfigKey.USERNAME]: getHTTPNormalizer(ConfigKey.USERNAME), - [ConfigKey.PASSWORD]: getHTTPNormalizer(ConfigKey.PASSWORD), - [ConfigKey.PROXY_URL]: getHTTPNormalizer(ConfigKey.PROXY_URL), - [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: getHTTPJsonToJavascriptNormalizer( - ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE - ), - [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: getHTTPJsonToJavascriptNormalizer( - ConfigKey.RESPONSE_BODY_CHECK_POSITIVE - ), - [ConfigKey.RESPONSE_BODY_INDEX]: getHTTPNormalizer(ConfigKey.RESPONSE_BODY_INDEX), - [ConfigKey.RESPONSE_HEADERS_CHECK]: getHTTPJsonToJavascriptNormalizer( - ConfigKey.RESPONSE_HEADERS_CHECK - ), - [ConfigKey.RESPONSE_HEADERS_INDEX]: getHTTPNormalizer(ConfigKey.RESPONSE_HEADERS_INDEX), - [ConfigKey.RESPONSE_STATUS_CHECK]: getHTTPJsonToJavascriptNormalizer( - ConfigKey.RESPONSE_STATUS_CHECK - ), - [ConfigKey.REQUEST_BODY_CHECK]: (fields) => { - const requestBody = fields?.[ConfigKey.REQUEST_BODY_CHECK]?.value; - const requestHeaders = fields?.[ConfigKey.REQUEST_HEADERS_CHECK]?.value; - if (requestBody) { - const headers = requestHeaders - ? parseJsonIfString(fields?.[ConfigKey.REQUEST_HEADERS_CHECK]?.value) - : defaultHTTPAdvancedFields[ConfigKey.REQUEST_HEADERS_CHECK]; - const requestBodyValue = - requestBody !== null && requestBody !== undefined - ? JSON.parse(requestBody) - : defaultHTTPAdvancedFields[ConfigKey.REQUEST_BODY_CHECK]?.value; - let requestBodyType = defaultHTTPAdvancedFields[ConfigKey.REQUEST_BODY_CHECK]?.type; - Object.keys(headers || []).some((headerKey) => { - if (headerKey === 'Content-Type' && contentTypesToMode[headers[headerKey] as ContentType]) { - requestBodyType = contentTypesToMode[headers[headerKey] as ContentType]; - return true; - } - }); - return { - value: requestBodyValue, - type: requestBodyType, - }; - } else { - return defaultHTTPAdvancedFields[ConfigKey.REQUEST_BODY_CHECK]; - } - }, - [ConfigKey.REQUEST_HEADERS_CHECK]: getHTTPJsonToJavascriptNormalizer( - ConfigKey.REQUEST_HEADERS_CHECK - ), - [ConfigKey.REQUEST_METHOD_CHECK]: getHTTPNormalizer(ConfigKey.REQUEST_METHOD_CHECK), - ...commonNormalizers, - ...tlsNormalizers, -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/normalizers.ts deleted file mode 100644 index e954d1f4f66e..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/normalizers.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ICMPFields, ConfigKey } from '../types'; -import { - Normalizer, - commonNormalizers, - getNormalizer, - getCronNormalizer, -} from '../common/normalizers'; -import { defaultICMPSimpleFields } from '../contexts'; - -export type ICMPNormalizerMap = Record; - -export const getICMPNormalizer = (key: ConfigKey) => { - return getNormalizer(key, defaultICMPSimpleFields); -}; - -export const getICMPCronToSecondsNormalizer = (key: ConfigKey) => { - return getCronNormalizer(key, defaultICMPSimpleFields); -}; - -export const icmpNormalizers: ICMPNormalizerMap = { - [ConfigKey.HOSTS]: getICMPNormalizer(ConfigKey.HOSTS), - [ConfigKey.WAIT]: getICMPCronToSecondsNormalizer(ConfigKey.WAIT), - ...commonNormalizers, -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_edit_policy_extension_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_edit_policy_extension_wrapper.test.tsx new file mode 100644 index 000000000000..8a9b8c92e3b6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_edit_policy_extension_wrapper.test.tsx @@ -0,0 +1,361 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import 'jest-canvas-mock'; + +import React from 'react'; +import { render } from '../../lib/helper/rtl_helpers'; +import { NewPackagePolicy } from '@kbn/fleet-plugin/public'; +import { SyntheticsPolicyEditExtensionWrapper } from './synthetics_policy_edit_extension_wrapper'; + +// ensures that fields appropriately match to their label +jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + ...jest.requireActual('@elastic/eui/lib/services/accessibility/html_id_generator'), + htmlIdGenerator: () => () => `id-${Math.random()}`, +})); + +// ensures that fields appropriately match to their label +jest.mock('@elastic/eui/lib/services/accessibility', () => ({ + ...jest.requireActual('@elastic/eui/lib/services/accessibility'), + useGeneratedHtmlId: () => `id-${Math.random()}`, +})); + +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + { + props.onChange(e.jsonContent); + }} + /> + ), + }; +}); + +const defaultNewPolicy: NewPackagePolicy = { + name: 'samplePolicyName', + description: '', + namespace: 'default', + policy_id: 'ae774160-8e49-11eb-aba5-99269d21ba6e', + enabled: true, + inputs: [ + { + type: 'synthetics/http', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'http', + }, + vars: { + __ui: { + value: JSON.stringify({ is_tls_enabled: true }), + type: 'yaml', + }, + type: { + value: 'http', + type: 'text', + }, + name: { + value: 'Sample name', + type: 'text', + }, + schedule: { + value: '"@every 3m"', + type: 'text', + }, + urls: { + value: '', + type: 'text', + }, + 'service.name': { + value: '', + type: 'text', + }, + timeout: { + value: '16s', + type: 'text', + }, + max_redirects: { + value: 0, + type: 'integer', + }, + proxy_url: { + value: '', + type: 'text', + }, + tags: { + value: '[]', + type: 'yaml', + }, + 'response.include_headers': { + value: true, + type: 'bool', + }, + 'response.include_body': { + value: 'on_error', + type: 'text', + }, + 'check.request.method': { + value: 'GET', + type: 'text', + }, + 'check.request.headers': { + value: '{}', + type: 'yaml', + }, + 'check.request.body': { + value: '""', + type: 'yaml', + }, + 'check.response.status': { + value: '[]', + type: 'yaml', + }, + 'check.response.headers': { + value: '{}', + type: 'yaml', + }, + 'check.response.body.positive': { + value: '[]', + type: 'yaml', + }, + 'check.response.body.negative': { + value: '[]', + type: 'yaml', + }, + 'ssl.certificate_authorities': { + value: '', + type: 'yaml', + }, + 'ssl.certificate': { + value: '', + type: 'yaml', + }, + 'ssl.key': { + value: '', + type: 'yaml', + }, + 'ssl.key_passphrase': { + type: 'text', + }, + 'ssl.verification_mode': { + value: 'full', + type: 'text', + }, + }, + }, + ], + }, + { + type: 'synthetics/tcp', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { + type: 'synthetics', + dataset: 'tcp', + }, + vars: { + type: { + value: 'tcp', + type: 'text', + }, + name: { + type: 'text', + }, + schedule: { + value: '"@every 5s"', + type: 'text', + }, + hosts: { + type: 'text', + }, + 'service.name': { + type: 'text', + }, + timeout: { + type: 'text', + }, + max_redirects: { + type: 'integer', + }, + proxy_url: { + type: 'text', + }, + proxy_use_local_resolver: { + value: false, + type: 'bool', + }, + tags: { + type: 'yaml', + }, + 'check.send': { + type: 'text', + }, + 'check.receive': { + value: '', + type: 'yaml', + }, + 'ssl.certificate_authorities': { + type: 'yaml', + }, + 'ssl.certificate': { + type: 'yaml', + }, + 'ssl.key': { + type: 'yaml', + }, + 'ssl.key_passphrase': { + type: 'text', + }, + 'ssl.verification_mode': { + type: 'text', + }, + }, + }, + ], + }, + { + type: 'synthetics/icmp', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { + type: 'synthetics', + dataset: 'icmp', + }, + vars: { + type: { + value: 'icmp', + type: 'text', + }, + name: { + type: 'text', + }, + schedule: { + value: '"@every 5s"', + type: 'text', + }, + wait: { + value: '1s', + type: 'text', + }, + hosts: { + type: 'text', + }, + 'service.name': { + type: 'text', + }, + timeout: { + type: 'text', + }, + max_redirects: { + type: 'integer', + }, + tags: { + type: 'yaml', + }, + }, + }, + ], + }, + { + type: 'synthetics/browser', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { + type: 'synthetics', + dataset: 'browser', + }, + vars: { + type: { + value: 'browser', + type: 'text', + }, + name: { + value: 'Sample name', + type: 'text', + }, + schedule: { + value: '"@every 5s"', + type: 'text', + }, + 'source.zip_url.url': { + type: 'text', + }, + 'source.zip_url.username': { + type: 'text', + }, + 'source.zip_url.password': { + type: 'password', + }, + 'source.zip_url.folder': { + type: 'text', + }, + 'source.inline.script': { + type: 'yaml', + }, + timeout: { + type: 'text', + }, + tags: { + type: 'yaml', + }, + }, + }, + ], + }, + ], + package: { + name: 'synthetics', + title: 'Elastic Synthetics', + version: '0.66.0', + }, +}; + +const defaultCurrentPolicy: any = { + ...defaultNewPolicy, + id: '', + revision: '', + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', +}; + +describe('', () => { + const onChange = jest.fn(); + const WrappedComponent = ({ policy = defaultCurrentPolicy, newPolicy = defaultNewPolicy }) => { + return ( + + ); + }; + + it('shows deprecation notice', async () => { + const { getByText } = render(); + + expect( + getByText('Synthetic Monitoring is now available out of the box in Synthetics') + ).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx index d699fcb203ed..dbd83dac27d0 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx @@ -20,7 +20,7 @@ export const defaultConfig: PolicyConfig = DEFAULT_FIELDS; * for use in the Ingest app create / edit package policy */ export const SyntheticsPolicyCreateExtension = memo( - ({ newPolicy, onChange }) => { + ({ newPolicy }) => { useTrackPageview({ app: 'fleet', path: 'syntheticsCreate' }); useTrackPageview({ app: 'fleet', path: 'syntheticsCreate', delay: 15000 }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx index 976c363ef656..0f055fe1fea1 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx @@ -8,7 +8,6 @@ import React, { memo } from 'react'; import { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public'; import { SyntheticsPolicyCreateExtension } from './synthetics_policy_create_extension'; -import { SyntheticsProviders } from './contexts'; /** * Exports Synthetics-specific package policy instructions @@ -16,10 +15,6 @@ import { SyntheticsProviders } from './contexts'; */ export const SyntheticsPolicyCreateExtensionWrapper = memo(({ newPolicy, onChange }) => { - return ( - - - - ); + return ; }); SyntheticsPolicyCreateExtensionWrapper.displayName = 'SyntheticsPolicyCreateExtensionWrapper'; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension.tsx deleted file mode 100644 index 771ac6ed8c88..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo } from 'react'; -import { PackagePolicyEditExtensionComponentProps } from '@kbn/fleet-plugin/public'; -import { useTrackPageview } from '@kbn/observability-plugin/public'; -import { usePolicyConfigContext } from './contexts'; -import { MonitorFields, PolicyConfig } from './types'; -import { CustomFields } from './custom_fields'; -import { useUpdatePolicy } from './hooks/use_update_policy'; -import { usePolicy } from './hooks/use_policy'; -import { validate } from './validation'; - -interface SyntheticsPolicyEditExtensionProps { - newPolicy: PackagePolicyEditExtensionComponentProps['newPolicy']; - onChange: PackagePolicyEditExtensionComponentProps['onChange']; - defaultConfig: Partial; -} - -/** - * Exports Synthetics-specific package policy instructions - * for use in the Fleet app create / edit package policy - */ -export const SyntheticsPolicyEditExtension = memo( - ({ newPolicy, onChange, defaultConfig }) => { - useTrackPageview({ app: 'fleet', path: 'syntheticsEdit' }); - useTrackPageview({ app: 'fleet', path: 'syntheticsEdit', delay: 15000 }); - - const { monitorType } = usePolicyConfigContext(); - const policyConfig: PolicyConfig = usePolicy(newPolicy.name); - - useUpdatePolicy({ - defaultConfig, - config: policyConfig[monitorType] as Partial, - newPolicy, - onChange, - validate, - monitorType, - }); - - return ; - } -); -SyntheticsPolicyEditExtension.displayName = 'SyntheticsPolicyEditExtension'; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx deleted file mode 100644 index a1bc5f57f591..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx +++ /dev/null @@ -1,1178 +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 'jest-canvas-mock'; - -import React from 'react'; -import { fireEvent, waitFor } from '@testing-library/react'; -import { render } from '../../lib/helper/rtl_helpers'; -import { NewPackagePolicy } from '@kbn/fleet-plugin/public'; -import { SyntheticsPolicyEditExtensionWrapper } from './synthetics_policy_edit_extension_wrapper'; -import { ConfigKey, DataStream, ScheduleUnit } from './types'; -import { defaultConfig } from './synthetics_policy_create_extension'; - -// ensures that fields appropriately match to their label -jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ - ...jest.requireActual('@elastic/eui/lib/services/accessibility/html_id_generator'), - htmlIdGenerator: () => () => `id-${Math.random()}`, -})); - -// ensures that fields appropriately match to their label -jest.mock('@elastic/eui/lib/services/accessibility', () => ({ - ...jest.requireActual('@elastic/eui/lib/services/accessibility'), - useGeneratedHtmlId: () => `id-${Math.random()}`, -})); - -jest.mock('@kbn/kibana-react-plugin/public', () => { - const original = jest.requireActual('@kbn/kibana-react-plugin/public'); - return { - ...original, - // Mocking CodeEditor, which uses React Monaco under the hood - CodeEditor: (props: any) => ( - { - props.onChange(e.jsonContent); - }} - /> - ), - }; -}); - -const defaultNewPolicy: NewPackagePolicy = { - name: 'samplePolicyName', - description: '', - namespace: 'default', - policy_id: 'ae774160-8e49-11eb-aba5-99269d21ba6e', - enabled: true, - inputs: [ - { - type: 'synthetics/http', - enabled: true, - streams: [ - { - enabled: true, - data_stream: { - type: 'synthetics', - dataset: 'http', - }, - vars: { - __ui: { - value: JSON.stringify({ is_tls_enabled: true }), - type: 'yaml', - }, - type: { - value: 'http', - type: 'text', - }, - name: { - value: 'Sample name', - type: 'text', - }, - schedule: { - value: '"@every 3m"', - type: 'text', - }, - urls: { - value: '', - type: 'text', - }, - 'service.name': { - value: '', - type: 'text', - }, - timeout: { - value: '16s', - type: 'text', - }, - max_redirects: { - value: 0, - type: 'integer', - }, - proxy_url: { - value: '', - type: 'text', - }, - tags: { - value: '[]', - type: 'yaml', - }, - 'response.include_headers': { - value: true, - type: 'bool', - }, - 'response.include_body': { - value: 'on_error', - type: 'text', - }, - 'check.request.method': { - value: 'GET', - type: 'text', - }, - 'check.request.headers': { - value: '{}', - type: 'yaml', - }, - 'check.request.body': { - value: '""', - type: 'yaml', - }, - 'check.response.status': { - value: '[]', - type: 'yaml', - }, - 'check.response.headers': { - value: '{}', - type: 'yaml', - }, - 'check.response.body.positive': { - value: '[]', - type: 'yaml', - }, - 'check.response.body.negative': { - value: '[]', - type: 'yaml', - }, - 'ssl.certificate_authorities': { - value: '', - type: 'yaml', - }, - 'ssl.certificate': { - value: '', - type: 'yaml', - }, - 'ssl.key': { - value: '', - type: 'yaml', - }, - 'ssl.key_passphrase': { - type: 'text', - }, - 'ssl.verification_mode': { - value: 'full', - type: 'text', - }, - }, - }, - ], - }, - { - type: 'synthetics/tcp', - enabled: false, - streams: [ - { - enabled: false, - data_stream: { - type: 'synthetics', - dataset: 'tcp', - }, - vars: { - type: { - value: 'tcp', - type: 'text', - }, - name: { - type: 'text', - }, - schedule: { - value: '"@every 5s"', - type: 'text', - }, - hosts: { - type: 'text', - }, - 'service.name': { - type: 'text', - }, - timeout: { - type: 'text', - }, - max_redirects: { - type: 'integer', - }, - proxy_url: { - type: 'text', - }, - proxy_use_local_resolver: { - value: false, - type: 'bool', - }, - tags: { - type: 'yaml', - }, - 'check.send': { - type: 'text', - }, - 'check.receive': { - value: '', - type: 'yaml', - }, - 'ssl.certificate_authorities': { - type: 'yaml', - }, - 'ssl.certificate': { - type: 'yaml', - }, - 'ssl.key': { - type: 'yaml', - }, - 'ssl.key_passphrase': { - type: 'text', - }, - 'ssl.verification_mode': { - type: 'text', - }, - }, - }, - ], - }, - { - type: 'synthetics/icmp', - enabled: false, - streams: [ - { - enabled: false, - data_stream: { - type: 'synthetics', - dataset: 'icmp', - }, - vars: { - type: { - value: 'icmp', - type: 'text', - }, - name: { - type: 'text', - }, - schedule: { - value: '"@every 5s"', - type: 'text', - }, - wait: { - value: '1s', - type: 'text', - }, - hosts: { - type: 'text', - }, - 'service.name': { - type: 'text', - }, - timeout: { - type: 'text', - }, - max_redirects: { - type: 'integer', - }, - tags: { - type: 'yaml', - }, - }, - }, - ], - }, - { - type: 'synthetics/browser', - enabled: false, - streams: [ - { - enabled: false, - data_stream: { - type: 'synthetics', - dataset: 'browser', - }, - vars: { - type: { - value: 'browser', - type: 'text', - }, - name: { - value: 'Sample name', - type: 'text', - }, - schedule: { - value: '"@every 5s"', - type: 'text', - }, - 'source.zip_url.url': { - type: 'text', - }, - 'source.zip_url.username': { - type: 'text', - }, - 'source.zip_url.password': { - type: 'password', - }, - 'source.zip_url.folder': { - type: 'text', - }, - 'source.inline.script': { - type: 'yaml', - }, - timeout: { - type: 'text', - }, - tags: { - type: 'yaml', - }, - }, - }, - ], - }, - ], - package: { - name: 'synthetics', - title: 'Elastic Synthetics', - version: '0.66.0', - }, -}; - -const defaultCurrentPolicy: any = { - ...defaultNewPolicy, - id: '', - revision: '', - updated_at: '', - updated_by: '', - created_at: '', - created_by: '', -}; - -const defaultHTTPConfig = defaultConfig[DataStream.HTTP]; -const defaultICMPConfig = defaultConfig[DataStream.ICMP]; -const defaultTCPConfig = defaultConfig[DataStream.TCP]; -const defaultBrowserConfig = defaultConfig[DataStream.BROWSER]; - -describe('', () => { - const onChange = jest.fn(); - const WrappedComponent = ({ policy = defaultCurrentPolicy, newPolicy = defaultNewPolicy }) => { - return ( - - ); - }; - - beforeEach(() => { - onChange.mockClear(); - }); - - it('renders SyntheticsPolicyEditExtension', async () => { - const { getByText, getByLabelText, queryByLabelText } = render(); - const url = getByLabelText('URL') as HTMLInputElement; - const proxyUrl = getByLabelText('Proxy URL') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const monitorIntervalUnit = getByLabelText('Unit') as HTMLInputElement; - const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; - const maxRedirects = getByLabelText('Max redirects') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; - const enableTLSConfig = getByLabelText('Enable TLS configuration') as HTMLInputElement; - expect(url).toBeInTheDocument(); - expect(url.value).toEqual(defaultHTTPConfig[ConfigKey.URLS]); - expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKey.PROXY_URL]); - expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].number); - expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].unit); - expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKey.APM_SERVICE_NAME]); - expect(maxRedirects).toBeInTheDocument(); - expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKey.MAX_REDIRECTS]}`); - expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKey.TIMEOUT]}`); - // expect TLS settings to be in the document when at least one tls key is populated - expect(enableTLSConfig.getAttribute('aria-checked')).toEqual('true'); - expect(verificationMode).toBeInTheDocument(); - expect(verificationMode.value).toEqual(`${defaultHTTPConfig[ConfigKey.TLS_VERIFICATION_MODE]}`); - - // ensure other monitor type options are not in the DOM - expect(queryByLabelText('Host')).not.toBeInTheDocument(); - expect(queryByLabelText('Wait in seconds')).not.toBeInTheDocument(); - - // ensure at least one http advanced option is present - const advancedOptionsButton = getByText('Advanced HTTP options'); - fireEvent.click(advancedOptionsButton); - await waitFor(() => { - expect(getByLabelText('Request method')).toBeInTheDocument(); - }); - }); - - it('does not allow user to edit monitor type', async () => { - const { queryByLabelText } = render(); - - expect(queryByLabelText('Monitor type')).not.toBeInTheDocument(); - }); - - it('handles updating fields', async () => { - const { getByLabelText } = render(); - const url = getByLabelText('URL') as HTMLInputElement; - const proxyUrl = getByLabelText('Proxy URL') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const monitorIntervalUnit = getByLabelText('Unit') as HTMLInputElement; - const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; - const maxRedirects = getByLabelText('Max redirects') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - - fireEvent.change(url, { target: { value: 'http://elastic.co' } }); - fireEvent.change(proxyUrl, { target: { value: 'http://proxy.co' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '1' } }); - fireEvent.change(monitorIntervalUnit, { target: { value: ScheduleUnit.MINUTES } }); - fireEvent.change(apmServiceName, { target: { value: 'APM Service' } }); - fireEvent.change(maxRedirects, { target: { value: '2' } }); - fireEvent.change(timeout, { target: { value: '3' } }); - - expect(url.value).toEqual('http://elastic.co'); - expect(proxyUrl.value).toEqual('http://proxy.co'); - expect(monitorIntervalNumber.value).toEqual('1'); - expect(monitorIntervalUnit.value).toEqual(ScheduleUnit.MINUTES); - expect(apmServiceName.value).toEqual('APM Service'); - expect(maxRedirects.value).toEqual('2'); - expect(timeout.value).toEqual('3'); - - await waitFor(() => { - expect(onChange).toBeCalledWith({ - isValid: true, - updatedPolicy: { - ...defaultNewPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - streams: [ - { - ...defaultNewPolicy.inputs[0].streams[0], - vars: { - ...defaultNewPolicy.inputs[0].streams[0].vars, - urls: { - value: 'http://elastic.co', - type: 'text', - }, - proxy_url: { - value: 'http://proxy.co', - type: 'text', - }, - schedule: { - value: '"@every 1m"', - type: 'text', - }, - 'service.name': { - value: 'APM Service', - type: 'text', - }, - max_redirects: { - value: '2', - type: 'integer', - }, - timeout: { - value: '3s', - type: 'text', - }, - }, - }, - ], - }, - defaultNewPolicy.inputs[1], - defaultNewPolicy.inputs[2], - defaultNewPolicy.inputs[3], - ], - }, - }); - }); - }); - - it('handles calling onChange', async () => { - const { getByLabelText } = render(); - const url = getByLabelText('URL') as HTMLInputElement; - - fireEvent.change(url, { target: { value: 'http://elastic.co' } }); - - await waitFor(() => { - expect(onChange).toBeCalledWith({ - isValid: true, - updatedPolicy: { - ...defaultNewPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - streams: [ - { - ...defaultNewPolicy.inputs[0].streams[0], - vars: { - ...defaultNewPolicy.inputs[0].streams[0].vars, - urls: { - value: 'http://elastic.co', - type: 'text', - }, - }, - }, - ], - }, - defaultNewPolicy.inputs[1], - defaultNewPolicy.inputs[2], - defaultNewPolicy.inputs[3], - ], - }, - }); - }); - }); - - it('handles http validation', async () => { - const { getByText, getByLabelText, queryByText } = render(); - - const url = getByLabelText('URL') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const maxRedirects = getByLabelText('Max redirects') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - - // create errors - fireEvent.change(url, { target: { value: '' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '-1' } }); - fireEvent.change(maxRedirects, { target: { value: '-1' } }); - fireEvent.change(timeout, { target: { value: '-1' } }); - - const urlError = getByText('URL is required'); - const monitorIntervalError = getByText('Monitor frequency is required'); - const maxRedirectsError = getByText('Max redirects must be 0 or greater'); - const timeoutError = getByText('Timeout must be greater than or equal to 0'); - - expect(urlError).toBeInTheDocument(); - expect(monitorIntervalError).toBeInTheDocument(); - expect(maxRedirectsError).toBeInTheDocument(); - expect(timeoutError).toBeInTheDocument(); - - // expect onChange to be called with isValid false - await waitFor(() => { - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: false, - }) - ); - }); - - // resolve errors - fireEvent.change(url, { target: { value: 'http://elastic.co' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '1' } }); - fireEvent.change(maxRedirects, { target: { value: '1' } }); - fireEvent.change(timeout, { target: { value: '1' } }); - - // expect onChange to be called with isValid true - await waitFor(() => { - expect(queryByText('URL is required')).not.toBeInTheDocument(); - expect(queryByText('Monitor frequency is required')).not.toBeInTheDocument(); - expect(queryByText('Max redirects must be 0 or greater')).not.toBeInTheDocument(); - expect(queryByText('Timeout must be greater than or equal to 0')).not.toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: true, - }) - ); - }); - }); - - it('shows tls fields when metadata.is_tls_enabled', async () => { - const currentPolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: true, - streams: [ - { - ...defaultNewPolicy.inputs[0].streams[0], - vars: { - ...defaultNewPolicy.inputs[0].streams[0].vars, - __ui: { - type: 'yaml', - value: JSON.stringify({ - is_tls_enabled: true, - }), - }, - }, - }, - ], - }, - ], - }; - - const { getByLabelText } = render(); - const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; - const enableTLSConfig = getByLabelText('Enable TLS configuration') as HTMLInputElement; - expect(enableTLSConfig.getAttribute('aria-checked')).toEqual('true'); - expect(verificationMode).toBeInTheDocument(); - expect(verificationMode.value).toEqual(`${defaultHTTPConfig[ConfigKey.TLS_VERIFICATION_MODE]}`); - }); - - it('handles browser validation', async () => { - const currentPolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[2], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[3], - enabled: true, - }, - ], - }; - const { getByText, getByLabelText, queryByText, getByRole, getByTestId } = render( - - ); - - const zip = getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - const zipUrl = getByRole('textbox', { name: 'Zip URL' }) as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - - // create errors - fireEvent.change(zipUrl, { target: { value: '' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '-1' } }); - - await waitFor(() => { - const hostError = getByText('Zip URL is required'); - const monitorIntervalError = getByText('Monitor frequency is required'); - - expect(hostError).toBeInTheDocument(); - expect(monitorIntervalError).toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: false, - }) - ); - }); - - await waitFor(() => { - fireEvent.change(zipUrl, { target: { value: 'http://github.com/tests.zip' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '2' } }); - expect(zipUrl.value).toEqual('http://github.com/tests.zip'); - expect(monitorIntervalNumber.value).toEqual('2'); - expect(queryByText('Zip URL is required')).not.toBeInTheDocument(); - expect(queryByText('Monitor frequency is required')).not.toBeInTheDocument(); - expect(queryByText('Timeout must be greater than or equal to 0')).not.toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: true, - }) - ); - }); - - await waitFor(() => { - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: true, - }) - ); - }); - }, 10000); - - it('handles tcp validation', async () => { - const currentPolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: true, - }, - defaultNewPolicy.inputs[2], - defaultNewPolicy.inputs[3], - ], - }; - const { getByText, getByLabelText, queryByText } = render( - - ); - - const host = getByLabelText('Host:Port') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - - // create errors - fireEvent.change(host, { target: { value: 'localhost' } }); // host without port - fireEvent.change(monitorIntervalNumber, { target: { value: '-1' } }); - fireEvent.change(timeout, { target: { value: '-1' } }); - - await waitFor(() => { - const hostError = getByText('Host and port are required'); - const monitorIntervalError = getByText('Monitor frequency is required'); - const timeoutError = getByText('Timeout must be greater than or equal to 0'); - - expect(hostError).toBeInTheDocument(); - expect(monitorIntervalError).toBeInTheDocument(); - expect(timeoutError).toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: false, - }) - ); - }); - - // resolve errors - fireEvent.change(host, { target: { value: 'smtp.gmail.com:587' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '1' } }); - fireEvent.change(timeout, { target: { value: '1' } }); - - await waitFor(() => { - expect(queryByText('Host is required')).not.toBeInTheDocument(); - expect(queryByText('Monitor frequency is required')).not.toBeInTheDocument(); - expect(queryByText('Timeout must be greater than or equal to 0')).not.toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: true, - }) - ); - }); - }); - - it('handles icmp validation', async () => { - const currentPolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[2], - enabled: true, - }, - defaultNewPolicy.inputs[3], - ], - }; - const { getByText, getByLabelText, queryByText } = render( - - ); - - const host = getByLabelText('Host') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - const wait = getByLabelText('Wait in seconds') as HTMLInputElement; - - // create errors - fireEvent.change(host, { target: { value: '' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '-1' } }); - fireEvent.change(timeout, { target: { value: '-1' } }); - fireEvent.change(wait, { target: { value: '-1' } }); - - await waitFor(() => { - const hostError = getByText('Host is required'); - const monitorIntervalError = getByText('Monitor frequency is required'); - const timeoutError = getByText('Timeout must be greater than or equal to 0'); - const waitError = getByText('Wait must be 0 or greater'); - - expect(hostError).toBeInTheDocument(); - expect(monitorIntervalError).toBeInTheDocument(); - expect(timeoutError).toBeInTheDocument(); - expect(waitError).toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: false, - }) - ); - }); - - // resolve errors - fireEvent.change(host, { target: { value: '1.1.1.1' } }); - fireEvent.change(monitorIntervalNumber, { target: { value: '1' } }); - fireEvent.change(timeout, { target: { value: '1' } }); - fireEvent.change(wait, { target: { value: '1' } }); - - await waitFor(() => { - expect(queryByText('Host is required')).not.toBeInTheDocument(); - expect(queryByText('Monitor frequency is required')).not.toBeInTheDocument(); - expect(queryByText('Timeout must be greater than or equal to 0')).not.toBeInTheDocument(); - expect(queryByText('Wait must be 0 or greater')).not.toBeInTheDocument(); - expect(onChange).toBeCalledWith( - expect.objectContaining({ - isValid: true, - }) - ); - }); - }); - - it('handles null values for http', async () => { - const httpVars = defaultNewPolicy.inputs[0].streams[0].vars; - const currentPolicy: NewPackagePolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: true, - streams: [ - { - ...defaultNewPolicy.inputs[0].streams[0], - vars: { - ...Object.keys(httpVars || []).reduce< - Record - >((acc, key) => { - acc[key] = { - value: undefined, - type: `${httpVars?.[key].type}`, - }; - return acc; - }, {}), - [ConfigKey.MONITOR_TYPE]: { - value: 'http', - type: 'text', - }, - }, - }, - ], - }, - defaultCurrentPolicy.inputs[1], - defaultCurrentPolicy.inputs[2], - defaultCurrentPolicy.inputs[3], - ], - }; - const { getByText, getByLabelText, queryByLabelText, queryByText } = render( - - ); - const url = getByLabelText('URL') as HTMLInputElement; - const proxyUrl = getByLabelText('Proxy URL') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const monitorIntervalUnit = getByLabelText('Unit') as HTMLInputElement; - const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; - const maxRedirects = getByLabelText('Max redirects') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - const enableTLSConfig = getByLabelText('Enable TLS configuration') as HTMLInputElement; - - expect(url).toBeInTheDocument(); - expect(url.value).toEqual(defaultHTTPConfig[ConfigKey.URLS]); - expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKey.PROXY_URL]); - expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].number); - expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].unit); - expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKey.APM_SERVICE_NAME]); - expect(maxRedirects).toBeInTheDocument(); - expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKey.MAX_REDIRECTS]}`); - expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKey.TIMEOUT]}`); - - /* expect TLS settings not to be in the document when and Enable TLS settings not to be checked - * when all TLS values are falsey */ - expect(enableTLSConfig.getAttribute('aria-checked')).toEqual('false'); - expect(queryByText('Verification mode')).not.toBeInTheDocument(); - - // ensure other monitor type options are not in the DOM - expect(queryByLabelText('Host')).not.toBeInTheDocument(); - expect(queryByLabelText('Wait in seconds')).not.toBeInTheDocument(); - - // ensure at least one http advanced option is present - const advancedOptionsButton = getByText('Advanced HTTP options'); - fireEvent.click(advancedOptionsButton); - await waitFor(() => { - const requestMethod = getByLabelText('Request method') as HTMLInputElement; - expect(requestMethod).toBeInTheDocument(); - expect(requestMethod.value).toEqual(`${defaultHTTPConfig[ConfigKey.REQUEST_METHOD_CHECK]}`); - }); - }); - - it('handles null values for tcp', async () => { - const tcpVars = defaultNewPolicy.inputs[1].streams[0].vars; - const currentPolicy: NewPackagePolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: true, - streams: [ - { - ...defaultNewPolicy.inputs[1].streams[0], - vars: { - ...Object.keys(tcpVars || []).reduce< - Record - >((acc, key) => { - acc[key] = { - value: undefined, - type: `${tcpVars?.[key].type}`, - }; - return acc; - }, {}), - [ConfigKey.MONITOR_TYPE]: { - value: DataStream.TCP, - type: 'text', - }, - }, - }, - ], - }, - defaultCurrentPolicy.inputs[2], - ], - }; - const { getByText, getByLabelText, queryByLabelText } = render( - - ); - const host = getByLabelText('Host:Port') as HTMLInputElement; - const proxyUrl = getByLabelText('Proxy URL') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const monitorIntervalUnit = getByLabelText('Unit') as HTMLInputElement; - const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - expect(host).toBeInTheDocument(); - expect(host.value).toEqual(defaultTCPConfig[ConfigKey.HOSTS]); - expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultTCPConfig[ConfigKey.PROXY_URL]); - expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultTCPConfig[ConfigKey.SCHEDULE].number); - expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultTCPConfig[ConfigKey.SCHEDULE].unit); - expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultTCPConfig[ConfigKey.APM_SERVICE_NAME]); - expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultTCPConfig[ConfigKey.TIMEOUT]}`); - - // ensure other monitor type options are not in the DOM - expect(queryByLabelText('Url')).not.toBeInTheDocument(); - expect(queryByLabelText('Wait in seconds')).not.toBeInTheDocument(); - - // ensure at least one tcp advanced option is present - const advancedOptionsButton = getByText('Advanced TCP options'); - fireEvent.click(advancedOptionsButton); - await waitFor(() => { - expect(getByLabelText('Request payload')).toBeInTheDocument(); - }); - }); - - it('handles null values for icmp', async () => { - const icmpVars = defaultNewPolicy.inputs[2].streams[0].vars; - const currentPolicy: NewPackagePolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[2], - enabled: true, - streams: [ - { - ...defaultNewPolicy.inputs[2].streams[0], - vars: { - ...Object.keys(icmpVars || []).reduce< - Record - >((acc, key) => { - acc[key] = { - value: undefined, - type: `${icmpVars?.[key].type}`, - }; - return acc; - }, {}), - [ConfigKey.MONITOR_TYPE]: { - value: DataStream.ICMP, - type: 'text', - }, - }, - }, - ], - }, - ], - }; - const { getByLabelText, queryByLabelText } = render( - - ); - const host = getByLabelText('Host') as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const monitorIntervalUnit = getByLabelText('Unit') as HTMLInputElement; - const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; - const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - const wait = getByLabelText('Wait in seconds') as HTMLInputElement; - expect(host).toBeInTheDocument(); - expect(host.value).toEqual(defaultICMPConfig[ConfigKey.HOSTS]); - expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultICMPConfig[ConfigKey.SCHEDULE].number); - expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultICMPConfig[ConfigKey.SCHEDULE].unit); - expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultICMPConfig[ConfigKey.APM_SERVICE_NAME]); - expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultICMPConfig[ConfigKey.TIMEOUT]}`); - expect(wait).toBeInTheDocument(); - expect(wait.value).toEqual(`${defaultICMPConfig[ConfigKey.WAIT]}`); - - // ensure other monitor type options are not in the DOM - expect(queryByLabelText('Url')).not.toBeInTheDocument(); - expect(queryByLabelText('Proxy URL')).not.toBeInTheDocument(); - }); - - it('handles null values for browser', async () => { - const browserVars = defaultNewPolicy.inputs[3].streams[0].vars; - const currentPolicy: NewPackagePolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[2], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[3], - enabled: true, - streams: [ - { - ...defaultNewPolicy.inputs[3].streams[0], - vars: { - ...Object.keys(browserVars || []).reduce< - Record - >((acc, key) => { - acc[key] = { - value: undefined, - type: `${browserVars?.[key].type}`, - }; - return acc; - }, {}), - [ConfigKey.MONITOR_TYPE]: { - value: DataStream.BROWSER, - type: 'text', - }, - }, - }, - ], - }, - ], - }; - const { getByLabelText, queryByLabelText, getByRole, getByTestId } = render( - - ); - const zip = getByTestId('syntheticsSourceTab__zipUrl'); - fireEvent.click(zip); - const zipUrl = getByRole('textbox', { name: 'Zip URL' }) as HTMLInputElement; - const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; - const monitorIntervalUnit = getByLabelText('Unit') as HTMLInputElement; - const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; - expect(zipUrl).toBeInTheDocument(); - expect(zipUrl.value).toEqual(defaultBrowserConfig[ConfigKey.SOURCE_ZIP_URL]); - expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultBrowserConfig[ConfigKey.SCHEDULE].number); - expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultBrowserConfig[ConfigKey.SCHEDULE].unit); - expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultBrowserConfig[ConfigKey.APM_SERVICE_NAME]); - - // ensure other monitor type options are not in the DOM - expect(queryByLabelText('Url')).not.toBeInTheDocument(); - expect(queryByLabelText('Proxy URL')).not.toBeInTheDocument(); - expect(queryByLabelText('Host')).not.toBeInTheDocument(); - }); - - it.each([ - [true, 'Testing script'], - [false, 'Inline script'], - ])( - 'browser monitors - auto selects the right tab depending on source metadata', - async (isGeneratedScript, text) => { - const currentPolicy = { - ...defaultCurrentPolicy, - inputs: [ - { - ...defaultNewPolicy.inputs[0], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[1], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[2], - enabled: false, - }, - { - ...defaultNewPolicy.inputs[3], - enabled: true, - streams: [ - { - ...defaultNewPolicy.inputs[3].streams[0], - vars: { - ...defaultNewPolicy.inputs[3].streams[0].vars, - 'source.inline.script': { - type: 'yaml', - value: JSON.stringify('step(() => {})'), - }, - __ui: { - type: 'yaml', - value: JSON.stringify({ - script_source: { - is_generated_script: isGeneratedScript, - }, - }), - }, - }, - }, - ], - }, - ], - }; - - const { getByText } = render(); - - expect(getByText(text)).toBeInTheDocument(); - } - ); - - it('hides tls fields when metadata.is_tls_enabled is false', async () => { - const { getByLabelText, queryByLabelText } = render( - - ); - - const verificationMode = queryByLabelText('Verification mode'); - const enableTLSConfig = getByLabelText('Enable TLS configuration') as HTMLInputElement; - expect(enableTLSConfig.getAttribute('aria-checked')).toEqual('false'); - expect(verificationMode).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx index 00d5e8bd7b94..1f1be99eb5ff 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx @@ -5,28 +5,15 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiCallOut, EuiSpacer } from '@elastic/eui'; -import type { - FleetStartServices, - PackagePolicyEditExtensionComponentProps, -} from '@kbn/fleet-plugin/public'; +import type { FleetStartServices } from '@kbn/fleet-plugin/public'; +import { EuiButton, EuiCallOut } from '@elastic/eui'; +import type { PackagePolicyEditExtensionComponentProps } from '@kbn/fleet-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { useEditMonitorLocator } from '../../../apps/synthetics/hooks'; -import type { PolicyConfig, MonitorFields, TLSFields } from './types'; +import { DeprecateNoticeModal } from './deprecate_notice_modal'; import { ConfigKey, DataStream } from './types'; -import { SyntheticsPolicyEditExtension } from './synthetics_policy_edit_extension'; -import { - PolicyConfigContextProvider, - HTTPContextProvider, - TCPContextProvider, - ICMPSimpleFieldsContextProvider, - BrowserContextProvider, - TLSFieldsContextProvider, -} from './contexts'; -import { normalizers } from './helpers/normalizers'; -import { IntegrationDeprecationCallout } from '../overview/integration_deprecation/integration_deprecation_callout'; +import { useEditMonitorLocator } from '../../../apps/synthetics/hooks'; /** * Exports Synthetics-specific package policy instructions @@ -34,78 +21,23 @@ import { IntegrationDeprecationCallout } from '../overview/integration_deprecati */ export const SyntheticsPolicyEditExtensionWrapper = memo( ({ policy: currentPolicy, newPolicy, onChange }) => { - const { - enableTLS: isTLSEnabled, - enableZipUrlTLS: isZipUrlTLSEnabled, - fullConfig: fullDefaultConfig, - monitorTypeConfig: defaultConfig, - monitorType, - tlsConfig: defaultTLSConfig, - } = useMemo(() => { - let enableTLS = false; - let enableZipUrlTLS = false; - const getDefaultConfig = () => { - // find the enabled input to identify the current monitor type - const currentInput = currentPolicy.inputs.find((input) => input.enabled === true); - /* Inputs can have multiple data streams. This is true of the `synthetics/browser` input, which includes the browser.network and browser.screenshot - * data streams. The `browser.network` and `browser.screenshot` data streams are used to store metadata and mappings. - * However, the `browser` data stream is where the variables for the policy are stored. For this reason, we only want - * to grab the data stream that exists within our explicitly defined list, which is the browser data stream */ - const vars = currentInput?.streams.find((stream) => - Object.values(DataStream).includes(stream.data_stream.dataset as DataStream) - )?.vars; - - const type: DataStream = vars?.[ConfigKey.MONITOR_TYPE].value as DataStream; - - const configKeys: ConfigKey[] = Object.values(ConfigKey) || ([] as ConfigKey[]); - const formattedDefaultConfigForMonitorType: MonitorFields = - configKeys.reduce((acc: MonitorFields, key: ConfigKey) => { - return { - ...acc, - [key]: normalizers[key]?.(vars), - }; - }, {} as MonitorFields); + const { application } = useKibana().services; - const tlsConfig: TLSFields = { - [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: - formattedDefaultConfigForMonitorType[ConfigKey.TLS_CERTIFICATE_AUTHORITIES], - [ConfigKey.TLS_CERTIFICATE]: - formattedDefaultConfigForMonitorType[ConfigKey.TLS_CERTIFICATE], - [ConfigKey.TLS_KEY]: formattedDefaultConfigForMonitorType[ConfigKey.TLS_KEY], - [ConfigKey.TLS_KEY_PASSPHRASE]: - formattedDefaultConfigForMonitorType[ConfigKey.TLS_KEY_PASSPHRASE], - [ConfigKey.TLS_VERIFICATION_MODE]: - formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERIFICATION_MODE], - [ConfigKey.TLS_VERSION]: formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERSION], - }; + const { package: pkg } = newPolicy; - enableTLS = - formattedDefaultConfigForMonitorType[ConfigKey.METADATA]?.is_tls_enabled ?? - Boolean(vars?.[ConfigKey.TLS_VERIFICATION_MODE]?.value); - enableZipUrlTLS = - formattedDefaultConfigForMonitorType[ConfigKey.METADATA]?.is_zip_url_tls_enabled ?? - Boolean(vars?.[ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]?.value); - - const formattedDefaultConfig: Partial = { - [type]: formattedDefaultConfigForMonitorType, - }; - - return { - fullConfig: formattedDefaultConfig, - monitorTypeConfig: formattedDefaultConfigForMonitorType, - tlsConfig, - monitorType: type, - enableTLS, - enableZipUrlTLS, - }; - }; - - return getDefaultConfig(); - }, [currentPolicy]); + const onCancel = useCallback(() => { + application?.navigateToApp('integrations', { + path: `/detail/${pkg?.name}-${pkg?.version}/overview`, + }); + }, [application, pkg?.name, pkg?.version]); const locators = useKibana().services?.share?.url?.locators; + const currentInput = currentPolicy.inputs.find((input) => input.enabled === true); + const vars = currentInput?.streams.find((stream) => + Object.values(DataStream).includes(stream.data_stream.dataset as DataStream) + )?.vars; - const { config_id: configId } = defaultConfig; + const configId: string = vars?.[ConfigKey.CONFIG_ID]?.value as DataStream; const url = useEditMonitorLocator({ configId, locators }); @@ -118,34 +50,9 @@ export const SyntheticsPolicyEditExtensionWrapper = memo ); + } else { + return ; } - - return ( - - - - - - - - - - - - - - - - ); } ); SyntheticsPolicyEditExtensionWrapper.displayName = 'SyntheticsPolicyEditExtensionWrapper'; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts deleted file mode 100644 index 4fd671732260..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TCPFields, ConfigKey } from '../types'; -import { - Normalizer, - commonNormalizers, - getNormalizer, - getJsonToJavascriptNormalizer, -} from '../common/normalizers'; -import { tlsNormalizers } from '../tls/normalizers'; -import { defaultTCPSimpleFields, defaultTCPAdvancedFields } from '../contexts'; - -const defaultTCPFields = { - ...defaultTCPSimpleFields, - ...defaultTCPAdvancedFields, -}; - -export type TCPNormalizerMap = Record; - -export const getTCPNormalizer = (key: ConfigKey) => { - return getNormalizer(key, defaultTCPFields); -}; - -export const getTCPJsonToJavascriptNormalizer = (key: ConfigKey) => { - return getJsonToJavascriptNormalizer(key, defaultTCPFields); -}; - -export const tcpNormalizers: TCPNormalizerMap = { - [ConfigKey.METADATA]: getTCPJsonToJavascriptNormalizer(ConfigKey.METADATA), - [ConfigKey.HOSTS]: getTCPNormalizer(ConfigKey.HOSTS), - [ConfigKey.PORT]: getTCPNormalizer(ConfigKey.PORT), - [ConfigKey.PROXY_URL]: getTCPNormalizer(ConfigKey.PROXY_URL), - [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: getTCPNormalizer(ConfigKey.PROXY_USE_LOCAL_RESOLVER), - [ConfigKey.RESPONSE_RECEIVE_CHECK]: getTCPNormalizer(ConfigKey.RESPONSE_RECEIVE_CHECK), - [ConfigKey.REQUEST_SEND_CHECK]: getTCPNormalizer(ConfigKey.REQUEST_SEND_CHECK), - [ConfigKey.URLS]: getTCPNormalizer(ConfigKey.URLS), - ...tlsNormalizers, - ...commonNormalizers, -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts deleted file mode 100644 index 2a9ead81fce3..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ConfigKey, - DataStream, - HTTPFields, - MonitorFields, - ScheduleUnit, - SyntheticsMonitor, -} from '../../../../common/runtime_types'; -import { validate } from './validation'; - -describe('[Monitor Management] validation', () => { - const commonPropsValid: Partial = { - [ConfigKey.SCHEDULE]: { number: '5', unit: ScheduleUnit.MINUTES }, - [ConfigKey.TIMEOUT]: '3m', - }; - - describe('HTTP', () => { - const httpPropsValid: Partial = { - ...commonPropsValid, - [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '204'], - [ConfigKey.RESPONSE_HEADERS_CHECK]: { 'Content-Type': 'application/json' }, - [ConfigKey.REQUEST_HEADERS_CHECK]: { 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8' }, - [ConfigKey.MAX_REDIRECTS]: '3', - [ConfigKey.URLS]: 'https:// example-url.com', - }; - - it('should return false for all valid props', () => { - const validators = validate[DataStream.HTTP]; - const keysToValidate = [ - ConfigKey.SCHEDULE, - ConfigKey.TIMEOUT, - ConfigKey.RESPONSE_STATUS_CHECK, - ConfigKey.RESPONSE_HEADERS_CHECK, - ConfigKey.REQUEST_HEADERS_CHECK, - ConfigKey.MAX_REDIRECTS, - ConfigKey.URLS, - ]; - const validatorFns = keysToValidate.map((key) => validators[key]); - const result = validatorFns.map((fn) => fn?.(httpPropsValid) ?? true); - - expect(result).not.toEqual(expect.arrayContaining([true])); - }); - }); - - describe.each([ - [ConfigKey.SOURCE_INLINE, 'step(() => {});'], - [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], - ])('Browser', (configKey, value) => { - const browserProps = { - ...commonPropsValid, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, - [ConfigKey.TIMEOUT]: null, - [ConfigKey.URLS]: null, - [ConfigKey.PORT]: null, - [configKey]: value, - } as SyntheticsMonitor; - - it('should return false for all valid props', () => { - const validators = validate[DataStream.BROWSER]; - const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey]; - const validatorFns = keysToValidate.map((key) => validators[key]); - const result = validatorFns.map((fn) => fn?.(browserProps as Partial) ?? true); - - expect(result).not.toEqual(expect.arrayContaining([true])); - }); - }); - - // TODO: Add test for other monitor types if needed -}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.tsx deleted file mode 100644 index 396922c79e1f..000000000000 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.tsx +++ /dev/null @@ -1,160 +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 { ConfigKey, DataStream, ScheduleUnit, MonitorFields, Validator, Validation } from './types'; - -export const digitsOnly = /^[0-9]*$/g; -export const includesValidPort = /[^\:]+:[0-9]{1,5}$/g; - -type ValidationLibrary = Record; - -// returns true if invalid -function validateHeaders(headers: T): boolean { - return Object.keys(headers).some((key) => { - if (key) { - const whiteSpaceRegEx = /[\s]/g; - return whiteSpaceRegEx.test(key); - } else { - return false; - } - }); -} - -// returns true if invalid -const validateTimeout = ({ - scheduleNumber, - scheduleUnit, - timeout, -}: { - scheduleNumber: string; - scheduleUnit: ScheduleUnit; - timeout: string; -}): boolean => { - let schedule: number; - switch (scheduleUnit) { - case ScheduleUnit.SECONDS: - schedule = parseFloat(scheduleNumber); - break; - case ScheduleUnit.MINUTES: - schedule = parseFloat(scheduleNumber) * 60; - break; - default: - schedule = parseFloat(scheduleNumber); - } - - return parseFloat(timeout) > schedule; -}; - -// validation functions return true when invalid -const validateCommon: ValidationLibrary = { - [ConfigKey.SCHEDULE]: ({ [ConfigKey.SCHEDULE]: value }) => { - const { number, unit } = value as MonitorFields[ConfigKey.SCHEDULE]; - const parsedFloat = parseFloat(number); - return !parsedFloat || !unit || parsedFloat < 1; - }, - [ConfigKey.TIMEOUT]: ({ - [ConfigKey.MONITOR_TYPE]: monitorType, - [ConfigKey.TIMEOUT]: timeout, - [ConfigKey.SCHEDULE]: schedule, - }) => { - const { number, unit } = schedule as MonitorFields[ConfigKey.SCHEDULE]; - - // Timeout is not currently supported by browser monitors - if (monitorType === DataStream.BROWSER) { - return false; - } - - return ( - !timeout || - parseFloat(timeout) < 0 || - validateTimeout({ - timeout, - scheduleNumber: number, - scheduleUnit: unit, - }) - ); - }, -}; - -const validateHTTP: ValidationLibrary = { - [ConfigKey.RESPONSE_STATUS_CHECK]: ({ [ConfigKey.RESPONSE_STATUS_CHECK]: value }) => { - const statusCodes = value as MonitorFields[ConfigKey.RESPONSE_STATUS_CHECK]; - return statusCodes.length ? statusCodes.some((code) => !`${code}`.match(digitsOnly)) : false; - }, - [ConfigKey.RESPONSE_HEADERS_CHECK]: ({ [ConfigKey.RESPONSE_HEADERS_CHECK]: value }) => { - const headers = value as MonitorFields[ConfigKey.RESPONSE_HEADERS_CHECK]; - return validateHeaders(headers); - }, - [ConfigKey.REQUEST_HEADERS_CHECK]: ({ [ConfigKey.REQUEST_HEADERS_CHECK]: value }) => { - const headers = value as MonitorFields[ConfigKey.REQUEST_HEADERS_CHECK]; - return validateHeaders(headers); - }, - [ConfigKey.MAX_REDIRECTS]: ({ [ConfigKey.MAX_REDIRECTS]: value }) => - (!!value && !`${value}`.match(digitsOnly)) || - parseFloat(value as MonitorFields[ConfigKey.MAX_REDIRECTS]) < 0, - [ConfigKey.URLS]: ({ [ConfigKey.URLS]: value }) => !value, - ...validateCommon, -}; - -const validateTCP: Record = { - [ConfigKey.HOSTS]: ({ [ConfigKey.HOSTS]: value }) => { - return !value || !`${value}`.match(includesValidPort); - }, - ...validateCommon, -}; - -const validateICMP: ValidationLibrary = { - [ConfigKey.HOSTS]: ({ [ConfigKey.HOSTS]: value }) => !value, - [ConfigKey.WAIT]: ({ [ConfigKey.WAIT]: value }) => - !!value && - !digitsOnly.test(`${value}`) && - parseFloat(value as MonitorFields[ConfigKey.WAIT]) < 0, - ...validateCommon, -}; - -const validateThrottleValue = (speed: string | undefined, allowZero?: boolean) => { - if (speed === undefined || speed === '') return false; - const throttleValue = parseFloat(speed); - return isNaN(throttleValue) || (allowZero ? throttleValue < 0 : throttleValue <= 0); -}; - -export const validateParamsValue = (params?: string) => { - try { - if (params) { - JSON.parse(params ?? ''); - } - } catch (e) { - return true; - } - return false; -}; - -const validateBrowser: ValidationLibrary = { - ...validateCommon, - [ConfigKey.SOURCE_ZIP_URL]: ({ - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_INLINE]: inlineScript, - }) => !zipUrl && !inlineScript, - [ConfigKey.SOURCE_INLINE]: ({ - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_INLINE]: inlineScript, - }) => !zipUrl && !inlineScript, - [ConfigKey.DOWNLOAD_SPEED]: ({ [ConfigKey.DOWNLOAD_SPEED]: downloadSpeed }) => - validateThrottleValue(downloadSpeed), - [ConfigKey.UPLOAD_SPEED]: ({ [ConfigKey.UPLOAD_SPEED]: uploadSpeed }) => - validateThrottleValue(uploadSpeed), - [ConfigKey.LATENCY]: ({ [ConfigKey.LATENCY]: latency }) => validateThrottleValue(latency, true), - [ConfigKey.PARAMS]: ({ [ConfigKey.PARAMS]: params }) => validateParamsValue(params), -}; - -export type ValidateDictionary = Record; - -export const validate: ValidateDictionary = { - [DataStream.HTTP]: validateHTTP, - [DataStream.TCP]: validateTCP, - [DataStream.ICMP]: validateICMP, - [DataStream.BROWSER]: validateBrowser, -}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx index e80a305afa6f..04d86c8042c3 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx @@ -32,14 +32,12 @@ export const EditMonitorConfig = ({ monitor, throttling }: Props) => { const { enableTLS: isTLSEnabled, - enableZipUrlTLS: isZipUrlTLSEnabled, fullConfig: fullDefaultConfig, monitorTypeConfig: defaultConfig, monitorType, tlsConfig: defaultTLSConfig, } = useMemo(() => { let enableTLS = false; - let enableZipUrlTLS = false; const getDefaultConfig = () => { const type: DataStream = monitor[ConfigKey.MONITOR_TYPE] as DataStream; @@ -53,7 +51,6 @@ export const EditMonitorConfig = ({ monitor, throttling }: Props) => { }; enableTLS = Boolean(monitor[ConfigKey.METADATA]?.is_tls_enabled); - enableZipUrlTLS = Boolean(monitor[ConfigKey.METADATA]?.is_zip_url_tls_enabled); const formattedDefaultConfig: Partial = { [type]: monitor, @@ -65,7 +62,6 @@ export const EditMonitorConfig = ({ monitor, throttling }: Props) => { tlsConfig, monitorType: type, enableTLS, - enableZipUrlTLS, }; }; @@ -77,13 +73,11 @@ export const EditMonitorConfig = ({ monitor, throttling }: Props) => { policyDefaultValues={{ throttling, defaultIsTLSEnabled: isTLSEnabled, - defaultIsZipUrlTLSEnabled: isZipUrlTLSEnabled, defaultMonitorType: monitorType, defaultName: defaultConfig?.[ConfigKey.NAME] || '', // TODO - figure out typing concerns for name defaultNamespace: defaultConfig?.[ConfigKey.NAMESPACE] || DEFAULT_NAMESPACE_STRING, defaultLocations: defaultConfig[ConfigKey.LOCATIONS], isEditable: true, - isZipUrlSourceEnabled: false, allowedScheduleUnits: [ScheduleUnit.MINUTES], runsOnService: true, sourceType: monitor[ConfigKey.MONITOR_SOURCE_TYPE] || SourceType.UI, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts index dc2e5cb48c07..616041ad1bd9 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts @@ -92,10 +92,7 @@ describe('[Monitor Management] validation', () => { }); }); - describe.each([ - [ConfigKey.SOURCE_INLINE, 'step(() => {});'], - [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], - ])('Browser', (configKey, value) => { + describe.each([[ConfigKey.SOURCE_INLINE, 'step(() => {});']])('Browser', (configKey, value) => { const browserProps = { ...commonPropsValid, [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts index 7c6e09811d22..d4abfbd62d6f 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts @@ -5,7 +5,6 @@ * 2.0. */ import { isValidNamespace } from '@kbn/fleet-plugin/common'; -import { validateParamsValue } from '../fleet_package/validation'; import { ConfigKey, DataStream, @@ -30,6 +29,17 @@ function validateHeaders(headers: T): boolean { }); } +export const validateParamsValue = (params?: string) => { + try { + if (params) { + JSON.parse(params ?? ''); + } + } catch (e) { + return true; + } + return false; +}; + // returns true if invalid const validateTimeout = ({ scheduleNumber, @@ -142,14 +152,7 @@ const validateThrottleValue = (speed: string | undefined, allowZero?: boolean) = const validateBrowser: Validation = { ...validateCommon, - [ConfigKey.SOURCE_ZIP_URL]: ({ - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_INLINE]: inlineScript, - }) => !zipUrl && !inlineScript, - [ConfigKey.SOURCE_INLINE]: ({ - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_INLINE]: inlineScript, - }) => !zipUrl && !inlineScript, + [ConfigKey.SOURCE_INLINE]: ({ [ConfigKey.SOURCE_INLINE]: inlineScript }) => !inlineScript, [ConfigKey.DOWNLOAD_SPEED]: ({ [ConfigKey.DOWNLOAD_SPEED]: downloadSpeed }) => validateThrottleValue(downloadSpeed), [ConfigKey.UPLOAD_SPEED]: ({ [ConfigKey.UPLOAD_SPEED]: uploadSpeed }) => diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation.test.tsx index 68373ad85ba6..fa257ceb218a 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation.test.tsx @@ -15,7 +15,7 @@ import * as observabilityPublic from '@kbn/observability-plugin/public'; export const mockStorage = new StubBrowserStorage(); jest.mock('@kbn/observability-plugin/public'); -const DEPRECATION_TITLE = 'Migrate your Elastic Synthetics integration monitors before Elastic 8.8'; +const DEPRECATION_TITLE = 'Migrate your Elastic Synthetics integration monitors'; describe('IntegrationDeprecation', () => { const { FETCH_STATUS } = observabilityPublic; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation_callout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation_callout.tsx index 415334f15c24..17baeb741cbc 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation_callout.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/integration_deprecation/integration_deprecation_callout.tsx @@ -22,7 +22,7 @@ export function IntegrationDeprecationCallout({ title={ } color="warning" @@ -32,7 +32,7 @@ export function IntegrationDeprecationCallout({ { policyDefaultValues={{ throttling, runsOnService: true, - isZipUrlSourceEnabled: false, allowedScheduleUnits: [ScheduleUnit.MINUTES], }} > diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 68c4179861a1..7114cba4ea49 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -34401,7 +34401,6 @@ "xpack.synthetics.controls.selectSeverity.scoreDetailsDescription": "score {value} et supérieur", "xpack.synthetics.createMonitorRoute.title": "Créer le moniteur | {baseTitle}", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.throttling_exceeded.message": "Vous avez dépassé la limite de {throttlingField} pour les nœuds synthétiques. La valeur {throttlingField} ne peut pas être supérieure à {limit} Mbits/s.", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.deprecation.content": "L'URL du Zip est déclassée et sera supprimée dans une prochaine version. Utilisez les moniteurs de projet à la place pour créer des moniteurs à partir d'un référentiel distant et pour migrer les moniteurs d'URL de Zip existants. {link}", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.content": "Pour créer un moniteur \"Navigateur\", veuillez vous assurer que vous utilisez le conteneur Docker {agent}, qui inclut les dépendances permettant d'exécuter ces moniteurs. Pour en savoir plus, visitez notre {link}.", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.params.helpText": "Utilisez JSON pour définir les paramètres qui peuvent être référencés dans votre script avec {code}", "xpack.synthetics.deprecateNoticeModal.forMoreInformation": "Pour en savoir plus, {docsLink}", @@ -34779,14 +34778,8 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalTooltip": "Prévisualisez la méthode la plus rapide permettant de créer des scripts de monitoring Elastic Synthetics avec notre enregistreur Elastic Synthetics", "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.label": "Enregistreur de scripts", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.description": "Fournissez une configuration précise pour l'agent synthétique.", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.filtering.description": "Utilisez ces options pour appliquer les paramètres de moniteur sélectionnés à un sous-ensemble des tests de votre suite. Seul le sous-ensemble configuré sera exécuté par ce moniteur.", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.filtering.title": "Tests sélectifs", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.ignoreHttpsErrors.helpText": "Définissez cette option sur \"true\" pour désactiver la validation TLS/SSL dans le navigateur synthétique. Cette opération est utile pour tester les sites qui utilisent des certificats autosignés.", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.ignoreHttpsErrors.label": "Ignorer les erreurs HTTPS", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersMatch.helpText": "Exécutez uniquement les parcours dont le nom correspond au glob fourni avec ce moniteur.", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersMatch.label": "Filtrer la correspondance", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersTags.helpText": "Exécutez uniquement les parcours en utilisant les balises données avec ce moniteur.", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersTags.label": "Filtrer les balises", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.screenshots.helpText": "Définissez cette option pour gérer les captures d'écran effectuées par l'agent synthétique.", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.screenshots.label": "Options de capture d'écran", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.syntheticsArgs.helpText": "Arguments supplémentaires à transmettre au package de l'agent synthétique. Accepte une liste de chaînes. Cela est utile dans des scénarios rares et ne devrait normalement pas avoir besoin d'être défini.", @@ -34806,7 +34799,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.upload.label": "Vitesse de chargement", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.title": "Options de l'agent synthétique", "xpack.synthetics.createPackagePolicy.stepConfigure.certificateSettings.enableSSLSettings.label": "Activer la configuration TLS", - "xpack.synthetics.createPackagePolicy.stepConfigure.certificateSettings.enableZipUrlSSLSettings.label": "Activer la configuration TLS pour l'URL du zip", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificate.helpText": "Certificat formaté PEM pour l'authentification du client TLS.", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificate.label": "certificat", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificateAuthorities.helpText": "Autorités de certificats personnalisés formatés PEM.", @@ -34865,8 +34857,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.inputVarFieldOptionalLabel": "Facultatif", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.APMServiceName.helpText": "Nom de service APM pour ce monitoring. Correspond au champ ECS service.name. Définissez-le lors du monitoring d'une application qui utilise également APM pour activer les intégrations entre les données Uptime et APM dans Kibana.", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.APMServiceName.label": "Nom de service APM", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.brower.proxyURL.label": "URL du zip du proxy", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.http.helpText": "Proxy HTTP pour l'URL du zip.", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.error": "Script obligatoire", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.helpText": "Exécute des scripts de tests synthétiques définis en ligne.", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.label": "Script en ligne", @@ -34877,19 +34867,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.invalidFileError": "Type de fichier non valide. Veuillez charger un fichier .js généré par l'enregistreur Elastic Synthetics.", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.label": "Sélectionner un fichier .js généré par l'enregistreur", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.parsingError": "Erreur lors du chargement du fichier. Veuillez charger un fichier .js généré par l'enregistreur Elastic Synthetics au format de script en ligne.", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.deprecation.title": "L'URL de zip est déclassée", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.error": "L'URL de zip est requise", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.helpText": "Emplacement du fichier zip du référentiel du projet synthétique.", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.label": "URL du zip", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.recorderLink": "Télécharger l'enregistreur Elastic Synthetics", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.removeScriptLabel": "Retirer le script", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.showScriptLabel": "Afficher le script", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlFolder.helpText": "Chemin de répertoire relatif d'emplacement des fichiers de parcours synthétiques dans le référentiel.", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlFolder.label": "Dossier", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlPassword.abel": "Mot de passe de l'URL du zip", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlPassword.helpText": "Mot de passe pour l'authentification avec le point de terminaison du zip.", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlUsername.helpText": "Nom d'utilisateur pour l'authentification avec le point de terminaison du zip.", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlUsername.label": "Nom d'utilisateur de l'URL du zip", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browserLabel": "Navigateur (version bêta)", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.enabled.helpText": "Désactivez cette configuration pour désactiver le moniteur.", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.enabled.label": "Activé", @@ -34903,7 +34880,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType": "Type de moniteur", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.link": "documentation Synthetics", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.title": "Exigence", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.zipUrl.deprecation.link": "En savoir plus", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.error": "Le type de moniteur est requis", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.params.label": "Paramètres", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.password.helpText": "Mot de passe pour l'authentification avec le serveur.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 24ae4e2592b2..11c83f035f1d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -34380,7 +34380,6 @@ "xpack.synthetics.controls.selectSeverity.scoreDetailsDescription": "スコア{value}以上", "xpack.synthetics.createMonitorRoute.title": "監視の作成 | {baseTitle}", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.throttling_exceeded.message": "Syntheticsノードの{throttlingField}上限を超えました。{throttlingField}値を{limit}Mbpsより大きくすることはできません。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.deprecation.content": "Zip URLは廃止予定であり、将来のバージョンでは削除されます。リモートリポジトリからモニターを作成して、既存のZip URLモニターを移行するのではなく、プロジェクトモニターを使用してください。{link}", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.content": "「Browser」モニターを作成するには、{agent}Dockerコンテナーを使用していることを確認します。これには、これらのモニターを実行するための依存関係が含まれています。詳細については、{link}をご覧ください。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.params.helpText": "JSONを使用して、{code}のスクリプトで参照できるパラメーターを定義します", "xpack.synthetics.deprecateNoticeModal.forMoreInformation": "詳細については、{docsLink}", @@ -34758,14 +34757,8 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalTooltip": "Elastic Synthetics Recorderを使用してElastic Synthetics監視スクリプトを作成する最も簡単な方法をプレビュー", "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.label": "スクリプトの記録", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.description": "Syntheticsエージェントの微調整された構成を提供します。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.filtering.description": "これらのオプションを使用すると、選択したモニター設定をスイートのテストのサブセットに適用します。構成されたサブセットのみがこのモニターによって実行されます。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.filtering.title": "選択テスト", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.ignoreHttpsErrors.helpText": "このオプションをtrueに設定すると、SyntheticsブラウザーでTLS/SSL検証を無効にします。これは自己署名証明書を使用するサイトのテストで役立ちます。", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.ignoreHttpsErrors.label": "HTTPSエラーを無視", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersMatch.helpText": "このモニターの指定されたglobと一致する名前のジャーニーのみを実行します。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersMatch.label": "一致のフィルター", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersTags.helpText": "このモニターの特定のタグのジャーニーのみを実行します。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersTags.label": "タグのフィルター", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.screenshots.helpText": "このオプションを設定すると、Syntheticsエージェントでキャプチャされたスクリーンショットを管理します。", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.screenshots.label": "スクリーンショットオプション", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.syntheticsArgs.helpText": "Syntheticsエージェントパッケージに渡す追加の引数。文字列のリストを取ります。これはごくまれなシナリオで有用ですが、通常は設定する必要がありません。", @@ -34785,7 +34778,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.upload.label": "アップロード速度", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.title": "Syntheticsエージェントオプション", "xpack.synthetics.createPackagePolicy.stepConfigure.certificateSettings.enableSSLSettings.label": "TLS構成を有効にする", - "xpack.synthetics.createPackagePolicy.stepConfigure.certificateSettings.enableZipUrlSSLSettings.label": "Zip URLのTLS構成を有効にする", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificate.helpText": "TLSクライアント認証用のPEM形式の証明書。", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificate.label": "証明書", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificateAuthorities.helpText": "PEM形式のカスタム認証局。", @@ -34844,8 +34836,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.inputVarFieldOptionalLabel": "オプション", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.APMServiceName.helpText": "このモニターのAPMサービス名。service.name ECSフィールドに対応します。APMを使用して、アップタイムとKibanaのAPMデータ間の統合を有効にするアプリを監視しているときには、これを設定します。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.APMServiceName.label": "APMサービス名", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.brower.proxyURL.label": "プロキシZip URL", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.http.helpText": "Zip URLのHTTPプロキシ。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.error": "スクリプトが必要です", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.helpText": "インラインで定義されたSyntheticテストスクリプトを実行します。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.label": "インラインスクリプト", @@ -34856,19 +34846,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.invalidFileError": "無効なファイルタイプです。Elastic Synthetics Recorderで生成された.jsファイルをアップロードしてください。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.label": "レコーダーで生成された.jsファイルを選択", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.parsingError": "ファイルのアップロードエラーです。インラインスクリプト形式でElastic Synthetics Recorderによって生成された.jsファイルをアップロードしてください。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.deprecation.title": "Zip URLは廃止予定です", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.error": "Zip URLは必須です", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.helpText": "Syntheticsプロジェクトリポジトリzipファイルの場所。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.label": "Zip URL", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.recorderLink": "Elastic Synthetics Recorderをダウンロード", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.removeScriptLabel": "スクリプトを削除", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.showScriptLabel": "スクリプトを表示", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlFolder.helpText": "Synthetic journeyファイルが配置されるリポジトリの相対ディレクトリパス。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlFolder.label": "フォルダー", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlPassword.abel": "Zip URLパスワード", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlPassword.helpText": "zipエンドポイントに対して認証するためのパスワード。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlUsername.helpText": "zipエンドポイントに対して認証するためのユーザー名。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlUsername.label": "Zip URLユーザー名", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browserLabel": "ブラウザー(ベータ)", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.enabled.helpText": "この構成をオフにすると、モニターが無効になります。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.enabled.label": "有効", @@ -34882,7 +34859,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType": "モニタータイプ", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.link": "Syntheticsドキュメンテーション", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.title": "要件", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.zipUrl.deprecation.link": "詳細", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.error": "モニタータイプは必須です", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.params.label": "パラメーター", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.password.helpText": "サーバーと認証するためのパスワード。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a7325f0d5575..aa75fdff53bd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -34396,7 +34396,6 @@ "xpack.synthetics.controls.selectSeverity.scoreDetailsDescription": "分数 {value} 及以上", "xpack.synthetics.createMonitorRoute.title": "创建监测 | {baseTitle}", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.throttling_exceeded.message": "您已超出 Synthetic 节点的 {throttlingField} 限制。{throttlingField} 值不能大于 {limit}Mbps。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.deprecation.content": "Zip URL 已弃用,将在未来版本中移除。请改用项目监测以从远程存储库创建监测并迁移现有 Zip URL 监测。{link}", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.content": "要创建“浏览器”监测,请确保使用 {agent} Docker 容器,其中包含运行这些监测的依赖项。有关更多信息,请访问我们的 {link}。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.params.helpText": "请使用 JSON 来定义可在您的脚本中通过 {code} 引用的参数", "xpack.synthetics.deprecateNoticeModal.forMoreInformation": "有关更多信息,{docsLink}", @@ -34774,14 +34773,8 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalTooltip": "预览通过 Elastic Synthetics 记录器创建 Elastic Synthetics 监测脚本的最快方式", "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.label": "脚本记录器", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.description": "为 Synthetics 代理提供微调的配置。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.filtering.description": "使用这些选项可将选定监测设置应用于您套件中的测试子集。仅配置的子集由此监测运行。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.filtering.title": "选择性测试", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.ignoreHttpsErrors.helpText": "将此选项设为 true 可在 Synthetics 浏览器中禁用 TLS/SSL 验证。这对于使用自签名证书的测试站点很有用。", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.ignoreHttpsErrors.label": "忽略 HTTPS 错误", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersMatch.helpText": "通过此监测仅运行名称与提供的 glob 匹配的过程。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersMatch.label": "筛选匹配", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersTags.helpText": "通过此监测仅运行具有给定标签的过程。", - "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.journeyFiltersTags.label": "筛选标签", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.screenshots.helpText": "设置此选项以管理 Synthetics 代理捕获的屏幕截图。", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.screenshots.label": "屏幕截图选项", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.syntheticsArgs.helpText": "要传递给 Synthetics 代理软件包的附加参数。取字符串列表。这在极少情况下有用,通常应不需要设置。", @@ -34801,7 +34794,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.upload.label": "上传速度", "xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.title": "Synthetics 代理选项", "xpack.synthetics.createPackagePolicy.stepConfigure.certificateSettings.enableSSLSettings.label": "启用 TLS 配置", - "xpack.synthetics.createPackagePolicy.stepConfigure.certificateSettings.enableZipUrlSSLSettings.label": "启用用于 Zip URL 的 TLS 配置", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificate.helpText": "用于 TLS 客户端身份验证的 PEM 格式证书。", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificate.label": "证书", "xpack.synthetics.createPackagePolicy.stepConfigure.certsField.certificateAuthorities.helpText": "PEM 格式自定义证书颁发机构。", @@ -34859,9 +34851,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.icmpAdvancedOptions": "高级 ICMP 选项", "xpack.synthetics.createPackagePolicy.stepConfigure.inputVarFieldOptionalLabel": "可选", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.APMServiceName.helpText": "此监测的 APM 服务名称。对应于 service.name ECS 字段。监测也使用 APM 的应用时设置此选项以启用 Kibana 中的 Uptime 和 APM 数据之间的集成。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.APMServiceName.label": "APM 服务名称", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.brower.proxyURL.label": "代理 Zip URL", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.http.helpText": "用于 Zip URL 的 HTTP 代理。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.error": "“脚本”必填", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.helpText": "运行内联定义的 Synthetics 测试脚本。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.inlineScript.label": "内联脚本", @@ -34872,19 +34861,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.invalidFileError": "文件类型无效。请上传由 Elastic Synthetics 记录器生成的 .js 文件。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.label": "选择记录器生成的 .js 文件", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.parsingError": "上传文件时出错。请上传 Elastic Synthetics 记录器以内联脚本格式生成的 .js 文件。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.deprecation.title": "Zip URL 已过时", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.error": "ZIP URL 必填", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.helpText": "Synthetics 项目存储库 zip 文件的位置。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.label": "Zip URL", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.recorderLink": "下载 Elastic Synthetics 记录器", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.removeScriptLabel": "移除脚本", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrl.showScriptLabel": "显示脚本", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlFolder.helpText": "合成旅程文件在存储库中所在的相对目录路径。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlFolder.label": "文件夹", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlPassword.abel": "Zip URL 密码", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlPassword.helpText": "用于在 zip 终端上进行身份验证的密码。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlUsername.helpText": "用于在 zip 终端上进行身份验证的用户名。", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.zipUrlUsername.label": "Zip URL 用户名", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browserLabel": "浏览器(公测版)", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.enabled.helpText": "关闭此配置以禁用监测。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.enabled.label": "已启用", @@ -34898,7 +34874,6 @@ "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType": "监测类型", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.link": "Synthetics 文档", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.warning.title": "要求", - "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.browser.zipUrl.deprecation.link": "了解详情", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorType.error": "“监测类型”必填。", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.params.label": "参数", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.password.helpText": "用于在服务器上进行身份验证的密码。", From 2cc12ceef5320bac193587750d5e44ab6fe6a8e2 Mon Sep 17 00:00:00 2001 From: Jeramy Soucy Date: Tue, 28 Mar 2023 09:31:02 -0400 Subject: [PATCH 2/8] [Saved Objects] Documents intended use of removeReferencesTo (#153711) ## Summary Updates comments for `removeReferencesTo` (SO Repository) and `authorizeRemoveReferences` (SO Security Extension) methods with remarks regarding the intended use and authorization. Currently the only use case for `removeReferencesTo` is the delete method of the tags client. If the authorization check is changed to authorize an update for each referencing object, lingering references in objects which the user is not authorized to update may be left behind when a tag is deleted. We will leave the current implementation in place until a decision about if & how to manage referential integrity occurs. This PR documents the current intended use case for `removeReferencesTo` as: "to provide clean up of any references to an object which is being deleted (e.g. deleting a tag)." See issue #135259 and discussion [here](https://github.com/elastic/kibana/issues/135259#issuecomment-1482515139), for background. --- .../src/saved_objects_repository.ts | 14 +++++++++++--- .../src/extensions/security.ts | 7 ++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts index 3c1dc224a21d..94905494b0ee 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts @@ -306,9 +306,17 @@ export interface ISavedObjectsRepository { /** * Updates all objects containing a reference to the given {type, id} tuple to remove the said reference. * - * @remarks Will throw a conflict error if the `update_by_query` operation returns any failure. In that case - * some references might have been removed, and some were not. It is the caller's responsibility - * to handle and fix this situation if it was to happen. + * @remarks + * Will throw a conflict error if the `update_by_query` operation returns any failure. In that case some + * references might have been removed, and some were not. It is the caller's responsibility to handle and fix + * this situation if it was to happen. + * + * Intended use is to provide clean up of any references to an object which is being deleted (e.g. deleting + * a tag). See discussion here: https://github.com/elastic/kibana/issues/135259#issuecomment-1482515139 + * + * When security is enabled, authorization for this method is based only on authorization to delete the object + * represented by the {type, id} tuple. Therefore it is recommended only to call this method for the intended + * use case. * * @param {string} type - the type of the object to remove references to * @param {string} id - the ID of the object to remove references to diff --git a/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts b/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts index 68637be38b6f..58bbb91ec7de 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts @@ -407,7 +407,12 @@ export interface ISavedObjectsSecurityExtension { ) => Promise>; /** - * Performs authorization for the REMOVE_REFERENCES security action + * Performs authorization for the REMOVE_REFERENCES security action. Checks for authorization + * to delete the object to which references are to be removed. In reality, the operation is an + * UPDATE to all objects that reference the given object, but the intended use for the + * removeReferencesTo method is to clean up any references to an object which is being deleted + * (e.g. deleting a tag). + * See discussion here: https://github.com/elastic/kibana/issues/135259#issuecomment-1482515139 * @param params the namespace and object to authorize * @returns CheckAuthorizationResult - the resulting authorization level and authorization map */ From f266a0cee2b9ad3a048182efb87060aeff582f5e Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 28 Mar 2023 10:13:31 -0400 Subject: [PATCH 3/8] [Response Ops][Alerting] Ensuring retries on all `esClient` calls when installing FAAD resources (#153650) ## Summary Audited all the calls to ES during FAAD resource installation and ensured that each call is wrapped within a `retryTransientEsErrors` call. This does not handle retrying resource installation during rule execution if initial installation fails. That will be covered in another issue. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../lib/create_concrete_write_index.test.ts | 67 ++++++++++++++++ .../lib/create_concrete_write_index.ts | 30 +++---- ...reate_or_update_component_template.test.ts | 78 +++++++++++++++++++ .../create_or_update_component_template.ts | 57 ++++++++------ .../create_or_update_index_template.test.ts | 15 ++++ .../lib/create_or_update_index_template.ts | 5 +- 6 files changed, 216 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.test.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.test.ts index b345e821be3f..a4cb6a26d376 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.test.ts @@ -167,6 +167,37 @@ describe('createConcreteWriteIndex', () => { expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); }); + it(`should retry getting index on transient ES error`, async () => { + clusterClient.indices.getAlias.mockImplementation(async () => ({})); + const error = new Error(`fail`) as EsError; + error.meta = { + body: { + error: { + type: 'resource_already_exists_exception', + }, + }, + }; + clusterClient.indices.create.mockRejectedValueOnce(error); + clusterClient.indices.get + .mockRejectedValueOnce(new EsErrors.ConnectionError('foo')) + .mockRejectedValueOnce(new EsErrors.TimeoutError('timeout')) + .mockImplementationOnce(async () => ({ + '.internal.alerts-test.alerts-default-000001': { + aliases: { '.alerts-test.alerts-default': { is_write_index: true } }, + }, + })); + + await createConcreteWriteIndex({ + logger, + esClient: clusterClient, + indexPatterns: IndexPatterns, + totalFieldsLimit: 2500, + }); + + expect(clusterClient.indices.get).toHaveBeenCalledTimes(3); + expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); + }); + it(`should log and throw error if ES throws resource_already_exists_exception error and existing index is not the write index`, async () => { clusterClient.indices.getAlias.mockImplementation(async () => ({})); const error = new Error(`fail`) as EsError; @@ -265,6 +296,42 @@ describe('createConcreteWriteIndex', () => { expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); }); + it(`should retry simulateIndexTemplate on transient ES errors`, async () => { + clusterClient.indices.getAlias.mockImplementation(async () => GetAliasResponse); + clusterClient.indices.simulateIndexTemplate + .mockRejectedValueOnce(new EsErrors.ConnectionError('foo')) + .mockRejectedValueOnce(new EsErrors.TimeoutError('timeout')) + .mockImplementation(async () => SimulateTemplateResponse); + + await createConcreteWriteIndex({ + logger, + esClient: clusterClient, + indexPatterns: IndexPatterns, + totalFieldsLimit: 2500, + }); + + expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(4); + }); + + it(`should retry getting alias on transient ES errors`, async () => { + clusterClient.indices.getAlias + .mockRejectedValueOnce(new EsErrors.ConnectionError('foo')) + .mockRejectedValueOnce(new EsErrors.TimeoutError('timeout')) + .mockImplementation(async () => GetAliasResponse); + clusterClient.indices.simulateIndexTemplate.mockImplementation( + async () => SimulateTemplateResponse + ); + + await createConcreteWriteIndex({ + logger, + esClient: clusterClient, + indexPatterns: IndexPatterns, + totalFieldsLimit: 2500, + }); + + expect(clusterClient.indices.getAlias).toHaveBeenCalledTimes(3); + }); + it(`should retry settings update on transient ES errors`, async () => { clusterClient.indices.getAlias.mockImplementation(async () => GetAliasResponse); clusterClient.indices.simulateIndexTemplate.mockImplementation( diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.ts index df32e021602c..31aface31291 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.ts @@ -68,9 +68,10 @@ const updateUnderlyingMapping = async ({ const { index, alias } = concreteIndexInfo; let simulatedIndexMapping: IndicesSimulateIndexTemplateResponse; try { - simulatedIndexMapping = await esClient.indices.simulateIndexTemplate({ - name: index, - }); + simulatedIndexMapping = await retryTransientEsErrors( + () => esClient.indices.simulateIndexTemplate({ name: index }), + { logger } + ); } catch (err) { logger.error( `Ignored PUT mappings for alias ${alias}; error generating simulated mappings: ${err.message}` @@ -149,10 +150,14 @@ export const createConcreteWriteIndex = async ({ try { // Specify both the index pattern for the backing indices and their aliases // The alias prevents the request from finding other namespaces that could match the -* pattern - const response = await esClient.indices.getAlias({ - index: indexPatterns.pattern, - name: indexPatterns.basePattern, - }); + const response = await retryTransientEsErrors( + () => + esClient.indices.getAlias({ + index: indexPatterns.pattern, + name: indexPatterns.basePattern, + }), + { logger } + ); concreteIndices = Object.entries(response).flatMap(([index, { aliases }]) => Object.entries(aliases).map(([aliasName, aliasProperties]) => ({ @@ -213,9 +218,7 @@ export const createConcreteWriteIndex = async ({ }, }, }), - { - logger, - } + { logger } ); } catch (error) { logger.error(`Error creating concrete write index - ${error.message}`); @@ -223,9 +226,10 @@ export const createConcreteWriteIndex = async ({ // something else created it so suppress the error. If it's not the write // index, that's bad, throw an error. if (error?.meta?.body?.error?.type === 'resource_already_exists_exception') { - const existingIndices = await esClient.indices.get({ - index: indexPatterns.name, - }); + const existingIndices = await retryTransientEsErrors( + () => esClient.indices.get({ index: indexPatterns.name }), + { logger } + ); if (!existingIndices[indexPatterns.name]?.aliases?.[indexPatterns.alias]?.is_write_index) { throw Error( `Attempted to create index: ${indexPatterns.name} as the write index for alias: ${indexPatterns.alias}, but the index already exists and is not the write index for the alias` diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.test.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.test.ts index 6eda12a6898e..1083807a29c9 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.test.ts @@ -188,4 +188,82 @@ describe('createOrUpdateComponentTemplate', () => { }, }); }); + + it(`should retry getIndexTemplate and putIndexTemplate on transient ES errors`, async () => { + clusterClient.cluster.putComponentTemplate.mockRejectedValueOnce( + new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 400, + body: { + error: { + root_cause: [ + { + type: 'illegal_argument_exception', + reason: + 'updating component template [.alerts-ecs-mappings] results in invalid composable template [.alerts-security.alerts-default-index-template] after templates are merged', + }, + ], + type: 'illegal_argument_exception', + reason: + 'updating component template [.alerts-ecs-mappings] results in invalid composable template [.alerts-security.alerts-default-index-template] after templates are merged', + caused_by: { + type: 'illegal_argument_exception', + reason: + 'composable template [.alerts-security.alerts-default-index-template] template after composition with component templates [.alerts-ecs-mappings, .alerts-security.alerts-mappings, .alerts-technical-mappings] is invalid', + caused_by: { + type: 'illegal_argument_exception', + reason: + 'invalid composite mappings for [.alerts-security.alerts-default-index-template]', + caused_by: { + type: 'illegal_argument_exception', + reason: 'Limit of total fields [1900] has been exceeded', + }, + }, + }, + }, + }, + }) + ) + ); + const existingIndexTemplate = { + name: 'test-template', + index_template: { + index_patterns: ['test*'], + composed_of: ['test-mappings'], + template: { + settings: { + auto_expand_replicas: '0-1', + hidden: true, + 'index.lifecycle': { + name: '.alerts-ilm-policy', + rollover_alias: `.alerts-empty-default`, + }, + 'index.mapping.total_fields.limit': 1800, + }, + mappings: { + dynamic: false, + }, + }, + }, + }; + clusterClient.indices.getIndexTemplate + .mockRejectedValueOnce(new EsErrors.ConnectionError('foo')) + .mockRejectedValueOnce(new EsErrors.TimeoutError('timeout')) + .mockResolvedValueOnce({ + index_templates: [existingIndexTemplate], + }); + clusterClient.indices.putIndexTemplate + .mockRejectedValueOnce(new EsErrors.ConnectionError('foo')) + .mockRejectedValueOnce(new EsErrors.TimeoutError('timeout')) + .mockResolvedValue({ acknowledged: true }); + await createOrUpdateComponentTemplate({ + logger, + esClient: clusterClient, + template: ComponentTemplate, + totalFieldsLimit: 2500, + }); + + expect(clusterClient.indices.getIndexTemplate).toHaveBeenCalledTimes(3); + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(3); + }); }); diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.ts index e9d2ca73ff3f..97cdef833adb 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_component_template.ts @@ -23,10 +23,14 @@ interface CreateOrUpdateComponentTemplateOpts { const getIndexTemplatesUsingComponentTemplate = async ( esClient: ElasticsearchClient, componentTemplateName: string, - totalFieldsLimit: number + totalFieldsLimit: number, + logger: Logger ) => { // Get all index templates and filter down to just the ones referencing this component template - const { index_templates: indexTemplates } = await esClient.indices.getIndexTemplate(); + const { index_templates: indexTemplates } = await retryTransientEsErrors( + () => esClient.indices.getIndexTemplate(), + { logger } + ); const indexTemplatesUsingComponentTemplate = (indexTemplates ?? []).filter( (indexTemplate: IndicesGetIndexTemplateIndexTemplateItem) => indexTemplate.index_template.composed_of.includes(componentTemplateName) @@ -34,19 +38,23 @@ const getIndexTemplatesUsingComponentTemplate = async ( await asyncForEach( indexTemplatesUsingComponentTemplate, async (template: IndicesGetIndexTemplateIndexTemplateItem) => { - await esClient.indices.putIndexTemplate({ - name: template.name, - body: { - ...template.index_template, - template: { - ...template.index_template.template, - settings: { - ...template.index_template.template?.settings, - 'index.mapping.total_fields.limit': totalFieldsLimit, + await retryTransientEsErrors( + () => + esClient.indices.putIndexTemplate({ + name: template.name, + body: { + ...template.index_template, + template: { + ...template.index_template.template, + settings: { + ...template.index_template.template?.settings, + 'index.mapping.total_fields.limit': totalFieldsLimit, + }, + }, }, - }, - }, - }); + }), + { logger } + ); } ); }; @@ -54,10 +62,11 @@ const getIndexTemplatesUsingComponentTemplate = async ( const createOrUpdateComponentTemplateHelper = async ( esClient: ElasticsearchClient, template: ClusterPutComponentTemplateRequest, - totalFieldsLimit: number + totalFieldsLimit: number, + logger: Logger ) => { try { - await esClient.cluster.putComponentTemplate(template); + await retryTransientEsErrors(() => esClient.cluster.putComponentTemplate(template), { logger }); } catch (error) { const reason = error?.meta?.body?.error?.caused_by?.caused_by?.caused_by?.reason; if (reason && reason.match(/Limit of total fields \[\d+\] has been exceeded/) != null) { @@ -68,10 +77,17 @@ const createOrUpdateComponentTemplateHelper = async ( // number of new ECS fields pushes the composed mapping above the limit, this error will // occur. We have to update the field limit inside the index template now otherwise we // can never update the component template - await getIndexTemplatesUsingComponentTemplate(esClient, template.name, totalFieldsLimit); + await getIndexTemplatesUsingComponentTemplate( + esClient, + template.name, + totalFieldsLimit, + logger + ); // Try to update the component template again - await esClient.cluster.putComponentTemplate(template); + await retryTransientEsErrors(() => esClient.cluster.putComponentTemplate(template), { + logger, + }); } else { throw error; } @@ -87,10 +103,7 @@ export const createOrUpdateComponentTemplate = async ({ logger.info(`Installing component template ${template.name}`); try { - await retryTransientEsErrors( - () => createOrUpdateComponentTemplateHelper(esClient, template, totalFieldsLimit), - { logger } - ); + await createOrUpdateComponentTemplateHelper(esClient, template, totalFieldsLimit, logger); } catch (err) { logger.error(`Error installing component template ${template.name} - ${err.message}`); throw err; diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts index c095274b539b..d4ce203a0d0e 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.test.ts @@ -137,6 +137,21 @@ describe('createOrUpdateIndexTemplate', () => { expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(3); }); + it(`should retry simulateTemplate on transient ES errors`, async () => { + clusterClient.indices.simulateTemplate + .mockRejectedValueOnce(new EsErrors.ConnectionError('foo')) + .mockRejectedValueOnce(new EsErrors.TimeoutError('timeout')) + .mockImplementation(async () => SimulateTemplateResponse); + clusterClient.indices.putIndexTemplate.mockResolvedValue({ acknowledged: true }); + await createOrUpdateIndexTemplate({ + logger, + esClient: clusterClient, + template: IndexTemplate, + }); + + expect(clusterClient.indices.simulateTemplate).toHaveBeenCalledTimes(3); + }); + it(`should log and throw error if max retries exceeded`, async () => { clusterClient.indices.simulateTemplate.mockImplementation(async () => SimulateTemplateResponse); clusterClient.indices.putIndexTemplate.mockRejectedValue(new EsErrors.ConnectionError('foo')); diff --git a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts index 0c5c22140318..a17fad2d875e 100644 --- a/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts +++ b/x-pack/plugins/alerting/server/alerts_service/lib/create_or_update_index_template.ts @@ -100,7 +100,10 @@ export const createOrUpdateIndexTemplate = async ({ let mappings: MappingTypeMapping = {}; try { // Simulate the index template to proactively identify any issues with the mapping - const simulateResponse = await esClient.indices.simulateTemplate(template); + const simulateResponse = await retryTransientEsErrors( + () => esClient.indices.simulateTemplate(template), + { logger } + ); mappings = simulateResponse.template.mappings; } catch (err) { logger.error( From b61334fc5d0b3e4fe5f958ee6202c496a8b56d1b Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 28 Mar 2023 10:31:52 -0400 Subject: [PATCH 4/8] feat(slo): Add APM service badge (#153580) --- .../slo_status_badge.stories.tsx | 5 +- .../slo_details/components/header_title.tsx | 14 ++-- ..._sli_apm_params_to_apm_app_deeplink_url.ts | 51 ------------- .../public/pages/slo_details/slo_details.tsx | 5 +- .../slo_active_alerts_badge.stories.tsx | 28 +++++++ .../badges/slo_active_alerts_badge.tsx | 60 +++++++++++++++ .../components/badges/slo_badges.stories.tsx | 7 +- .../slos/components/badges/slo_badges.tsx | 50 ++----------- .../slo_indicator_type_badge.stories.tsx | 7 +- .../badges/slo_indicator_type_badge.tsx | 74 +++++++++++++++++-- .../badges/slo_time_window_badge.stories.tsx | 7 +- .../badges/slo_time_window_badge.tsx | 10 +-- ...apm_params_to_apm_app_deeplink_url.test.ts | 28 +++---- ..._sli_apm_params_to_apm_app_deeplink_url.ts | 57 ++++++++++++++ .../public/utils/slo/indicator.ts | 11 +++ 15 files changed, 280 insertions(+), 134 deletions(-) delete mode 100644 x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.ts create mode 100644 x-pack/plugins/observability/public/pages/slos/components/badges/slo_active_alerts_badge.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slos/components/badges/slo_active_alerts_badge.tsx rename x-pack/plugins/observability/public/{pages/slo_details/helpers => utils/slo}/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts (54%) create mode 100644 x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.ts create mode 100644 x-pack/plugins/observability/public/utils/slo/indicator.ts diff --git a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_status_badge.stories.tsx b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_status_badge.stories.tsx index 1c1313beb15c..e8a76e8dc111 100644 --- a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_status_badge.stories.tsx +++ b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_status_badge.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; +import { EuiFlexGroup } from '@elastic/eui'; import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; import { SloStatusBadge as Component, SloStatusProps } from './slo_status_badge'; import { buildSlo } from '../../../data/slo/slo'; @@ -19,7 +20,9 @@ export default { }; const Template: ComponentStory = (props: SloStatusProps) => ( - + + + ); const defaultProps = { diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/header_title.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/header_title.tsx index d2448d8959ad..44e31dac6894 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/header_title.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/header_title.tsx @@ -22,14 +22,14 @@ export function HeaderTitle(props: Props) { return ; } + if (!slo) { + return null; + } + return ( - - {slo && slo.name} - {!!slo && ( - - - - )} + + {slo.name} + ); } diff --git a/x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.ts b/x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.ts deleted file mode 100644 index c90b64cb7355..000000000000 --- a/x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.ts +++ /dev/null @@ -1,51 +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. - */ - -interface Props { - duration: string; - environment: string; - filter: string | undefined; - service: string; - transactionName: string; - transactionType: string; -} - -export function convertSliApmParamsToApmAppDeeplinkUrl({ - duration, - environment, - filter, - service, - transactionName, - transactionType, -}: Props) { - if (!service) { - return ''; - } - - const environmentPartial = environment - ? `&environment=${environment === '*' ? 'ENVIRONMENT_ALL' : environment}` - : ''; - - const transactionTypePartial = transactionType - ? `&transactionType=${transactionType === '*' ? '' : transactionType}` - : ''; - - const dateRangePartial = duration ? `&rangeFrom=now-${duration}&rangeTo=now` : ''; - - const filterPartial = - filter || transactionName - ? `&kuery=${encodeURIComponent( - `${ - transactionName && transactionName !== '*' - ? `transaction.name : "${transactionName}"` - : '' - } ${filter ? `and ${filter}` : ''}` - )}` - : ''; - - return `/app/apm/services/${service}/overview?comparisonEnabled=true${environmentPartial}${transactionTypePartial}${filterPartial}${dateRangePartial}`; -} diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx index dc745ec35345..bbb606e76c07 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx @@ -22,10 +22,11 @@ import PageNotFound from '../404'; import { SloDetails } from './components/slo_details'; import { HeaderTitle } from './components/header_title'; import { HeaderControl } from './components/header_control'; -import { convertSliApmParamsToApmAppDeeplinkUrl } from './helpers/convert_sli_apm_params_to_apm_app_deeplink_url'; import { paths } from '../../config/paths'; import type { SloDetailsPathParams } from './types'; import type { ObservabilityAppServices } from '../../application/types'; +import { isApmIndicatorType } from '../../utils/slo/indicator'; +import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url'; export function SloDetailsPage() { const { @@ -80,7 +81,7 @@ export function SloDetailsPage() { pageTitle: , rightSideItems: [ , - slo?.indicator.type.includes('apm') ? ( + !!slo && isApmIndicatorType(slo.indicator.type) ? ( = (props: Props) => ( + + + +); + +export const Default = Template.bind({}); +Default.args = { activeAlerts: { count: 2, ruleIds: ['rule-1', 'rule-2'] } }; diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_active_alerts_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_active_alerts_badge.tsx new file mode 100644 index 000000000000..e64d4b2b67fc --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_active_alerts_badge.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 { EuiBadge, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { paths } from '../../../../config/paths'; +import { useKibana } from '../../../../utils/kibana_react'; + +import { ActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts'; +import { toAlertsPageQueryFilter } from '../../helpers/alerts_page_query_filter'; + +export interface Props { + activeAlerts?: ActiveAlerts; +} + +export function SloActiveAlertsBadge({ activeAlerts }: Props) { + const { + application: { navigateToUrl }, + http: { basePath }, + } = useKibana().services; + + const handleActiveAlertsClick = () => { + if (activeAlerts) { + navigateToUrl( + `${basePath.prepend(paths.observability.alerts)}?_a=${toAlertsPageQueryFilter( + activeAlerts + )}` + ); + } + }; + + if (!activeAlerts) { + return null; + } + + return ( + + + {i18n.translate('xpack.observability.slo.slo.activeAlertsBadge.label', { + defaultMessage: '{count, plural, one {# alert} other {# alerts}}', + values: { count: activeAlerts.count }, + })} + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx index f3abb871e6e2..50430e0f6bd0 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; +import { EuiFlexGroup } from '@elastic/eui'; import { buildForecastedSlo } from '../../../../data/slo/slo'; import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; import { SloBadges as Component, Props } from './slo_badges'; @@ -18,7 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => ; +const Template: ComponentStory = (props: Props) => ( + + + +); const defaultProps = { slo: buildForecastedSlo(), diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx index 1ad2a8892ae2..dacee509af08 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx @@ -6,17 +6,14 @@ */ import React from 'react'; -import { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; -import { i18n } from '@kbn/i18n'; -import { useKibana } from '../../../../utils/kibana_react'; import { SloIndicatorTypeBadge } from './slo_indicator_type_badge'; import { SloStatusBadge } from '../../../../components/slo/slo_status_badge'; import { SloTimeWindowBadge } from './slo_time_window_badge'; -import { toAlertsPageQueryFilter } from '../../helpers/alerts_page_query_filter'; -import { paths } from '../../../../config/paths'; import type { ActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts'; +import { SloActiveAlertsBadge } from './slo_active_alerts_badge'; export interface Props { slo: SLOWithSummaryResponse; @@ -24,49 +21,12 @@ export interface Props { } export function SloBadges({ slo, activeAlerts }: Props) { - const { - application: { navigateToUrl }, - http: { basePath }, - } = useKibana().services; - - const handleClick = () => { - if (activeAlerts) { - navigateToUrl( - `${basePath.prepend(paths.observability.alerts)}?_a=${toAlertsPageQueryFilter( - activeAlerts - )}` - ); - } - }; - return ( - - - - - - - {!!activeAlerts && ( - - - {i18n.translate('xpack.observability.slo.slo.activeAlertsBadge.label', { - defaultMessage: '{count, plural, one {# alert} other {# alerts}}', - values: { count: activeAlerts.count }, - })} - - - )} + + + ); } diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.stories.tsx index a8a3933be95d..fa451706de16 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; +import { EuiFlexGroup } from '@elastic/eui'; import { buildCustomKqlIndicator, buildApmAvailabilityIndicator, @@ -23,7 +24,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => ; +const Template: ComponentStory = (props: Props) => ( + + + +); export const WithCustomKql = Template.bind({}); WithCustomKql.args = { slo: buildSlo({ indicator: buildCustomKqlIndicator() }) }; diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx index 8a16a0e73fd0..87f005e2fa5c 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx @@ -6,21 +6,83 @@ */ import React from 'react'; -import { EuiBadge } from '@elastic/eui'; +import { EuiBadge, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { euiLightVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; +import { useKibana } from '../../../../utils/kibana_react'; +import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url'; +import { isApmIndicatorType } from '../../../../utils/slo/indicator'; import { toIndicatorTypeLabel } from '../../../../utils/slo/labels'; + export interface Props { slo: SLOWithSummaryResponse; } export function SloIndicatorTypeBadge({ slo }: Props) { + const { + application: { navigateToUrl }, + http: { basePath }, + } = useKibana().services; + + const handleNavigateToApm = () => { + if ( + slo.indicator.type === 'sli.apm.transactionDuration' || + slo.indicator.type === 'sli.apm.transactionErrorRate' + ) { + const { + indicator: { + params: { environment, filter, service, transactionName, transactionType }, + }, + timeWindow: { duration }, + } = slo; + + const url = convertSliApmParamsToApmAppDeeplinkUrl({ + duration, + environment, + filter, + service, + transactionName, + transactionType, + }); + + navigateToUrl(basePath.prepend(url)); + } + }; + return ( -
- - {toIndicatorTypeLabel(slo.indicator.type)} - -
+ <> + + + {toIndicatorTypeLabel(slo.indicator.type)} + + + {isApmIndicatorType(slo.indicator.type) && 'service' in slo.indicator.params && ( + + + + {slo.indicator.params.service} + + + + )} + ); } diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.stories.tsx index 361b839b7bf7..7f534e831f9f 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; +import { EuiFlexGroup } from '@elastic/eui'; import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; import { SloTimeWindowBadge as Component, Props } from './slo_time_window_badge'; import { buildSlo } from '../../../../data/slo/slo'; @@ -18,7 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => ; +const Template: ComponentStory = (props: Props) => ( + + + +); export const With7DaysRolling = Template.bind({}); With7DaysRolling.args = { slo: buildSlo({ timeWindow: { duration: '7d', isRolling: true } }) }; diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx index e7291a229239..aefec729896f 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx @@ -7,7 +7,7 @@ import moment from 'moment'; import React from 'react'; -import { EuiBadge } from '@elastic/eui'; +import { EuiBadge, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { euiLightVars } from '@kbn/ui-theme'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; @@ -24,7 +24,7 @@ export function SloTimeWindowBadge({ slo }: Props) { const unit = slo.timeWindow.duration.slice(-1); if ('isRolling' in slo.timeWindow) { return ( -
+ {toDurationLabel(slo.timeWindow.duration)} -
+ ); } @@ -50,7 +50,7 @@ export function SloTimeWindowBadge({ slo }: Props) { const elapsedDurationInDays = now.diff(periodStart, 'days') + 1; return ( -
+ {i18n.translate('xpack.observability.slo.slo.timeWindow.calendar', { defaultMessage: '{elapsed}/{total} days', @@ -60,6 +60,6 @@ export function SloTimeWindowBadge({ slo }: Props) { }, })} -
+ ); } diff --git a/x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts b/x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts similarity index 54% rename from x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts rename to x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts index f69cba40335c..11236c3d6bbd 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/helpers/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts +++ b/x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.test.ts @@ -10,7 +10,7 @@ import { convertSliApmParamsToApmAppDeeplinkUrl } from './convert_sli_apm_params const SLI_APM_PARAMS = { duration: '30-d', environment: 'fooEnvironment', - filter: 'agent.name : "beats" ', + filter: 'agent.name : "beats" and agent.version : 3.4.12 ', service: 'barService', transactionName: 'bazName', transactionType: 'blarfType', @@ -20,8 +20,8 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { it('should return a correct APM deeplink when all params have a value', () => { const url = convertSliApmParamsToApmAppDeeplinkUrl(SLI_APM_PARAMS); - expect(url).toBe( - '/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&kuery=transaction.name%20%3A%20%22bazName%22%20and%20agent.name%20%3A%20%22beats%22%20&rangeFrom=now-30-d&rangeTo=now' + expect(url).toMatchInlineSnapshot( + `"/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&rangeFrom=now-30-d&rangeTo=now&kuery=transaction.name+%3A+%22bazName%22+and+agent.name+%3A+%22beats%22+and+agent.version+%3A+3.4.12+"` ); }); @@ -31,8 +31,8 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { duration: '', }); - expect(url).toBe( - '/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&kuery=transaction.name%20%3A%20%22bazName%22%20and%20agent.name%20%3A%20%22beats%22%20' + expect(url).toMatchInlineSnapshot( + `"/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&kuery=transaction.name+%3A+%22bazName%22+and+agent.name+%3A+%22beats%22+and+agent.version+%3A+3.4.12+"` ); }); @@ -42,8 +42,8 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { environment: '', }); - expect(url).toBe( - '/app/apm/services/barService/overview?comparisonEnabled=true&transactionType=blarfType&kuery=transaction.name%20%3A%20%22bazName%22%20and%20agent.name%20%3A%20%22beats%22%20&rangeFrom=now-30-d&rangeTo=now' + expect(url).toMatchInlineSnapshot( + `"/app/apm/services/barService/overview?comparisonEnabled=true&transactionType=blarfType&rangeFrom=now-30-d&rangeTo=now&kuery=transaction.name+%3A+%22bazName%22+and+agent.name+%3A+%22beats%22+and+agent.version+%3A+3.4.12+"` ); }); @@ -53,8 +53,8 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { filter: '', }); - expect(url).toBe( - '/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&kuery=transaction.name%20%3A%20%22bazName%22%20&rangeFrom=now-30-d&rangeTo=now' + expect(url).toMatchInlineSnapshot( + `"/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&rangeFrom=now-30-d&rangeTo=now&kuery=transaction.name+%3A+%22bazName%22"` ); }); @@ -64,7 +64,7 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { service: '', }); - expect(url).toBe(''); + expect(url).toMatchInlineSnapshot(`""`); }); it('should return a correct APM deeplink when empty transactionName is passed', () => { @@ -73,8 +73,8 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { transactionName: '', }); - expect(url).toBe( - '/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&kuery=%20and%20agent.name%20%3A%20%22beats%22%20&rangeFrom=now-30-d&rangeTo=now' + expect(url).toMatchInlineSnapshot( + `"/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&transactionType=blarfType&rangeFrom=now-30-d&rangeTo=now&kuery=agent.name+%3A+%22beats%22+and+agent.version+%3A+3.4.12+"` ); }); @@ -84,8 +84,8 @@ describe('convertSliApmParamsToApmAppDeeplinkUrl', () => { transactionType: '', }); - expect(url).toBe( - '/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&kuery=transaction.name%20%3A%20%22bazName%22%20and%20agent.name%20%3A%20%22beats%22%20&rangeFrom=now-30-d&rangeTo=now' + expect(url).toMatchInlineSnapshot( + `"/app/apm/services/barService/overview?comparisonEnabled=true&environment=fooEnvironment&rangeFrom=now-30-d&rangeTo=now&kuery=transaction.name+%3A+%22bazName%22+and+agent.name+%3A+%22beats%22+and+agent.version+%3A+3.4.12+"` ); }); }); diff --git a/x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.ts b/x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.ts new file mode 100644 index 000000000000..d15096cc59a0 --- /dev/null +++ b/x-pack/plugins/observability/public/utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface Props { + duration: string; + environment: string; + filter: string | undefined; + service: string; + transactionName: string; + transactionType: string; +} + +export function convertSliApmParamsToApmAppDeeplinkUrl({ + duration, + environment, + filter, + service, + transactionName, + transactionType, +}: Props) { + if (!service) { + return ''; + } + + const qs = new URLSearchParams('comparisonEnabled=true'); + + if (environment) { + qs.append('environment', environment === '*' ? 'ENVIRONMENT_ALL' : environment); + } + + if (transactionType) { + qs.append('transactionType', transactionType === '*' ? '' : transactionType); + } + + if (duration) { + qs.append('rangeFrom', `now-${duration}`); + qs.append('rangeTo', 'now'); + } + + const kueryParams = []; + if (transactionName && transactionName !== '*') { + kueryParams.push(`transaction.name : "${transactionName}"`); + } + if (filter && filter.length > 0) { + kueryParams.push(filter); + } + + if (kueryParams.length > 0) { + qs.append('kuery', kueryParams.join(' and ')); + } + + return `/app/apm/services/${service}/overview?${qs.toString()}`; +} diff --git a/x-pack/plugins/observability/public/utils/slo/indicator.ts b/x-pack/plugins/observability/public/utils/slo/indicator.ts new file mode 100644 index 000000000000..955eab9e3329 --- /dev/null +++ b/x-pack/plugins/observability/public/utils/slo/indicator.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; + +export const isApmIndicatorType = (indicatorType: SLOWithSummaryResponse['indicator']['type']) => + ['sli.apm.transactionDuration', 'sli.apm.transactionErrorRate'].includes(indicatorType); From e99c741b0de7ffea3c6920b95eea82e627cb173d Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:55:45 -0400 Subject: [PATCH 5/8] [Security Solution] Skip flaky Endpoint suites (#153870) ## Summary Skipping more Endpoint suites, tracking here: https://github.com/elastic/kibana/issues/153855 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/endpoint/artifact_entries_list.ts | 3 ++- .../security_solution_endpoint/apps/endpoint/endpoint_list.ts | 3 ++- .../apps/endpoint/endpoint_permissions.ts | 3 ++- .../apps/endpoint/endpoint_solution_integrations.ts | 3 ++- .../security_solution_endpoint/apps/endpoint/policy_details.ts | 3 ++- .../test/security_solution_endpoint/apps/endpoint/responder.ts | 3 ++- .../apps/endpoint/trusted_apps_list.ts | 3 ++- .../apis/endpoint_response_actions/execute.ts | 3 ++- .../test/security_solution_endpoint_api_int/apis/metadata.ts | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/artifact_entries_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/artifact_entries_list.ts index b52db3c2c266..00ff378cde2b 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/artifact_entries_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/artifact_entries_list.ts @@ -22,7 +22,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esClient = getService('es'); const unzipPromisify = promisify(unzip); - describe('For each artifact list under management', function () { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('For each artifact list under management', function () { let indexedData: IndexedHostsAndAlertsResponse; const checkFleetArtifacts = async ( diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index cc46e4c1ebf9..f25f6be49183 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -81,7 +81,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return tableData; }; - describe('endpoint list', function () { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('endpoint list', function () { const sleep = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)); let indexedData: IndexedHostsAndAlertsResponse; describe('when initially navigating to page', () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_permissions.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_permissions.ts index 4b2abd052043..f76765c3b67e 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_permissions.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_permissions.ts @@ -20,7 +20,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const endpointTestResources = getService('endpointTestResources'); const policyTestResources = getService('policyTestResources'); - describe('Endpoint permissions:', () => { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('Endpoint permissions:', () => { let indexedData: IndexedHostsAndAlertsResponse; before(async () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts index 32b2ce196d7e..8f4e71be82b9 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts @@ -24,7 +24,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'timeline']); - describe('App level Endpoint functionality', () => { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('App level Endpoint functionality', () => { let indexedData: IndexedHostsAndAlertsResponse; let indexedAlerts: IndexedEndpointRuleAlerts; let endpointAgentId: string; diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index da4b75412638..d46c9a997dcb 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -25,7 +25,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const policyTestResources = getService('policyTestResources'); const endpointTestResources = getService('endpointTestResources'); - describe('When on the Endpoint Policy Details Page', function () { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('When on the Endpoint Policy Details Page', function () { let indexedData: IndexedHostsAndAlertsResponse; before(async () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts index 9081ca2ba096..bd8d8075cb74 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/responder.ts @@ -81,7 +81,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); }; - describe('Response Actions Responder', function () { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('Response Actions Responder', function () { let indexedData: IndexedHostsAndAlertsResponse; let endpointAgentId: string; diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts index 53e1e8f6ad7f..00c196c1b58f 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts @@ -16,7 +16,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const endpointTestResources = getService('endpointTestResources'); const policyTestResources = getService('policyTestResources'); - describe('When on the Trusted Apps list', function () { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('When on the Trusted Apps list', function () { let indexedData: IndexedHostsAndAlertsResponse; before(async () => { const endpointPackage = await policyTestResources.getEndpointPackage(); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts index 413c8bd69e82..8ac14f74afe5 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts @@ -15,7 +15,8 @@ export default function ({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const endpointTestResources = getService('endpointTestResources'); - describe('Endpoint `execute` response action', () => { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('Endpoint `execute` response action', () => { let indexedData: IndexedHostsAndAlertsResponse; let agentId = ''; diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts index 48b0107a599a..70b5a32c2464 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts @@ -37,7 +37,8 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const endpointTestResources = getService('endpointTestResources'); - describe('test metadata apis', () => { + // FLAKY: https://github.com/elastic/kibana/issues/153855 + describe.skip('test metadata apis', () => { before(async () => { await endpointTestResources.setMetadataTransformFrequency('1s'); }); From 52ca1a87bc0f7bff7b58714f127a2c389fb46618 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Tue, 28 Mar 2023 10:59:43 -0400 Subject: [PATCH 6/8] [Synthetics/Alerting] Add a param flag to hide the interval controls from edit form (#152607) ## Summary Fixes #152430. We need to be able to hide from the UI the capability of the user to edit the interval for our rule in the edit form. To achieve this, I've simply added a flag to the rule's `params` field, and picked that flag up on the form to hide the elements in question. Shouldn't have any impact elsewhere for users of this form. If there's a better method of achieving this please let me know and I'll revise. --- .../default_status_alert.journey.ts | 4 +- .../alerts/hooks/use_synthetics_alert.ts | 1 + .../sections/rule_form/rule_add.tsx | 2 + .../sections/rule_form/rule_form.tsx | 92 ++++++++++--------- .../triggers_actions_ui/public/types.ts | 2 + 5 files changed, 55 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts index 666366b9555e..ae8ffeed8343 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts @@ -58,8 +58,6 @@ journey(`DefaultStatusAlert`, async ({ page, params }) => { await page.isDisabled(byTestId('xpack.synthetics.toggleAlertFlyout')); await page.click(byTestId('xpack.synthetics.toggleAlertFlyout')); await page.waitForSelector('text=Edit rule'); - await page.selectOption(byTestId('intervalInputUnit'), { label: 'second' }); - await page.fill(byTestId('intervalInput'), '20'); await page.click(byTestId('saveEditedRuleButton')); await page.waitForSelector("text=Updated 'Synthetics internal alert'"); }); @@ -94,6 +92,8 @@ journey(`DefaultStatusAlert`, async ({ page, params }) => { await page.click(byTestId('syntheticsMonitorManagementTab')); await page.click(byTestId('syntheticsMonitorOverviewTab')); + await page.waitForTimeout(5 * 1000); + const totalDown = await page.textContent( byTestId('xpack.uptime.synthetics.overview.status.down') ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/hooks/use_synthetics_alert.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/hooks/use_synthetics_alert.ts index 3b1ec20345ae..2f7dd459bc92 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/hooks/use_synthetics_alert.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/hooks/use_synthetics_alert.ts @@ -39,6 +39,7 @@ export const useSyntheticsAlert = (isOpen: boolean) => { } return triggersActionsUi.getEditRuleFlyout({ onClose: () => dispatch(setAlertFlyoutVisible(false)), + hideInterval: true, initialRule: alert, }); }, [alert, dispatch, triggersActionsUi]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx index ec1f298b58ba..d27abe083144 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx @@ -48,6 +48,7 @@ const RuleAdd = ({ initialValues, reloadRules, onSave, + hideInterval, metadata: initialMetadata, filteredRuleTypes, ...props @@ -264,6 +265,7 @@ const RuleAdd = ({ ruleTypeRegistry={ruleTypeRegistry} metadata={metadata} filteredRuleTypes={filteredRuleTypes} + hideInterval={hideInterval} onChangeMetaData={onChangeMetaData} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index 40a8d7f5722e..7105ab930beb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -97,6 +97,7 @@ interface RuleFormProps> { setHasActionsWithBrokenConnector?: (value: boolean) => void; metadata?: MetaData; filteredRuleTypes?: string[]; + hideInterval?: boolean; connectorFeatureId?: string; onChangeMetaData: (metadata: MetaData) => void; } @@ -114,6 +115,7 @@ export const RuleForm = ({ actionTypeRegistry, metadata, filteredRuleTypes: ruleTypeToFilter, + hideInterval, connectorFeatureId = AlertingConnectorFeatureId, onChangeMetaData, }: RuleFormProps) => { @@ -582,50 +584,52 @@ export const RuleForm = ({ ) : null} - - 0} - error={errors['schedule.interval']} - > - - - 0} - value={ruleInterval || ''} - name="interval" - data-test-subj="intervalInput" - onChange={(e) => { - const value = e.target.value; - if (value === '' || INTEGER_REGEX.test(value)) { - const parsedValue = value === '' ? '' : parseInt(value, 10); - setRuleInterval(parsedValue || undefined); - setScheduleProperty('interval', `${parsedValue}${ruleIntervalUnit}`); - } - }} - /> - - - { - setRuleIntervalUnit(e.target.value); - setScheduleProperty('interval', `${ruleInterval}${e.target.value}`); - }} - data-test-subj="intervalInputUnit" - /> - - - - + {hideInterval !== true && ( + + 0} + error={errors['schedule.interval']} + > + + + 0} + value={ruleInterval || ''} + name="interval" + data-test-subj="intervalInput" + onChange={(e) => { + const value = e.target.value; + if (value === '' || INTEGER_REGEX.test(value)) { + const parsedValue = value === '' ? '' : parseInt(value, 10); + setRuleInterval(parsedValue || undefined); + setScheduleProperty('interval', `${parsedValue}${ruleIntervalUnit}`); + } + }} + /> + + + { + setRuleIntervalUnit(e.target.value); + setScheduleProperty('interval', `${ruleInterval}${e.target.value}`); + }} + data-test-subj="intervalInputUnit" + /> + + + + + )} {canShowActions && defaultActionGroupId && diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 05cf37aecad8..763d96105a30 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -408,6 +408,7 @@ export interface RuleEditProps> { onClose: (reason: RuleFlyoutCloseReason, metadata?: MetaData) => void; /** @deprecated use `onSave` as a callback after an alert is saved*/ reloadRules?: () => Promise; + hideInterval?: boolean; onSave?: (metadata?: MetaData) => Promise; metadata?: MetaData; ruleType?: RuleType; @@ -423,6 +424,7 @@ export interface RuleAddProps> { initialValues?: Partial; /** @deprecated use `onSave` as a callback after an alert is saved*/ reloadRules?: () => Promise; + hideInterval?: boolean; onSave?: (metadata?: MetaData) => Promise; metadata?: MetaData; ruleTypeIndex?: RuleTypeIndex; From 32371a4a8c8d4d7f51098e74c59682ecea3f5bb9 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 28 Mar 2023 17:04:44 +0200 Subject: [PATCH 7/8] [Synthetics] Make uses of spaces plugin optional (#153793) --- x-pack/plugins/synthetics/kibana.jsonc | 3 ++- .../plugins/synthetics/public/hooks/use_kibana_space.tsx | 3 ++- x-pack/plugins/synthetics/public/plugin.ts | 2 +- .../legacy_uptime/lib/adapters/framework/adapter_types.ts | 4 ++-- .../synthetics/server/routes/monitor_cruds/add_monitor.ts | 7 ++++--- .../server/routes/monitor_cruds/add_monitor_project.ts | 8 ++++++-- .../routes/monitor_cruds/add_monitor_project_legacy.ts | 7 +++++-- .../monitor_cruds/bulk_cruds/delete_monitor_bulk.ts | 6 +++++- .../server/routes/monitor_cruds/delete_monitor.ts | 3 ++- .../server/routes/monitor_cruds/edit_monitor.ts | 3 ++- .../synthetics/server/routes/settings/add_param.ts | 3 ++- .../plugins/synthetics/server/routes/settings/params.ts | 3 ++- .../server/routes/settings/sync_global_params.ts | 3 ++- .../server/routes/synthetics_service/run_once_monitor.ts | 3 ++- .../server/routes/synthetics_service/test_now_monitor.ts | 3 ++- 15 files changed, 41 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/synthetics/kibana.jsonc b/x-pack/plugins/synthetics/kibana.jsonc index 3236a730f1a5..036ee19d52ff 100644 --- a/x-pack/plugins/synthetics/kibana.jsonc +++ b/x-pack/plugins/synthetics/kibana.jsonc @@ -30,7 +30,6 @@ "triggersActionsUi", "usageCollection", "unifiedSearch", - "spaces", "bfetch" ], "optionalPlugins": [ @@ -39,6 +38,7 @@ "fleet", "home", "ml", + "spaces", "telemetry" ], "requiredBundles": [ @@ -49,6 +49,7 @@ "kibanaUtils", "ml", "observability", + "spaces", "indexLifecycleManagement" ] } diff --git a/x-pack/plugins/synthetics/public/hooks/use_kibana_space.tsx b/x-pack/plugins/synthetics/public/hooks/use_kibana_space.tsx index 1a4b8e31e122..bdb87133441d 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_kibana_space.tsx +++ b/x-pack/plugins/synthetics/public/hooks/use_kibana_space.tsx @@ -7,6 +7,7 @@ import type { Space } from '@kbn/spaces-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useFetcher } from '@kbn/observability-plugin/public'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { ClientPluginsStart } from '../plugin'; export const useKibanaSpace = () => { @@ -17,7 +18,7 @@ export const useKibanaSpace = () => { loading, error, } = useFetcher>(() => { - return services.spaces?.getActiveSpace(); + return services.spaces?.getActiveSpace() ?? Promise.resolve({ id: DEFAULT_SPACE_ID } as Space); }, [services.spaces]); return { diff --git a/x-pack/plugins/synthetics/public/plugin.ts b/x-pack/plugins/synthetics/public/plugin.ts index 46ecafb97559..2ac4aee91d9d 100644 --- a/x-pack/plugins/synthetics/public/plugin.ts +++ b/x-pack/plugins/synthetics/public/plugin.ts @@ -80,7 +80,7 @@ export interface ClientPluginsStart { triggersActionsUi: TriggersAndActionsUIPublicPluginStart; cases: CasesUiStart; dataViews: DataViewsPublicPluginStart; - spaces: SpacesPluginStart; + spaces?: SpacesPluginStart; cloud?: CloudStart; appName: string; storage: IStorageWrapper; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts index ec77b83977a0..ba1779d1c69f 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts @@ -54,7 +54,7 @@ export interface UptimeServerSetup { router: UptimeRouter; config: UptimeConfig; cloud?: CloudSetup; - spaces: SpacesPluginStart; + spaces?: SpacesPluginStart; fleet: FleetStartContract; security: SecurityPluginStart; savedObjectsClient?: SavedObjectsClientContract; @@ -89,5 +89,5 @@ export interface UptimeCorePluginsStart { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; taskManager: TaskManagerStartContract; telemetry: TelemetryPluginStart; - spaces: SpacesPluginStart; + spaces?: SpacesPluginStart; } diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts index 0db555a4c48b..c1c1c1648565 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts @@ -13,6 +13,7 @@ import { SavedObjectsErrorHelpers, } from '@kbn/core/server'; import { isValidNamespace } from '@kbn/fleet-plugin/common'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { getSyntheticsPrivateLocations } from '../../legacy_uptime/lib/saved_objects/private_locations'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; import { @@ -77,7 +78,7 @@ export const addSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ ); try { - const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const { errors, newMonitor } = await syncNewMonitor({ normalizedMonitor: validationResult.decodedMonitor, server, @@ -239,12 +240,12 @@ export const syncNewMonitor = async ({ } }; -export const getMonitorNamespace = ( +const getMonitorNamespace = ( server: UptimeServerSetup, request: KibanaRequest, configuredNamespace: string ) => { - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const kibanaNamespace = formatKibanaNamespace(spaceId); const namespace = configuredNamespace === DEFAULT_NAMESPACE_STRING ? kibanaNamespace : configuredNamespace; diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts index 11dbe95952fe..59f798b5bc62 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { ProjectMonitor } from '../../../common/runtime_types'; import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types'; @@ -51,7 +52,10 @@ export const addSyntheticsProjectMonitorRoute: SyntheticsRestApiRouteFactory = ( } try { - const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); + const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? { + id: DEFAULT_SPACE_ID, + }; + const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); const pushMonitorFormatter = new ProjectMonitorFormatter({ @@ -75,7 +79,7 @@ export const addSyntheticsProjectMonitorRoute: SyntheticsRestApiRouteFactory = ( } catch (error) { server.logger.error(`Error adding monitors to project ${decodedProjectName}`); if (error.output.statusCode === 404) { - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } }); } diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts index b68d2ac7aa58..cfb1ca2f680e 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts @@ -5,6 +5,7 @@ * 2.0. */ import { schema } from '@kbn/config-schema'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { UMServerLibs } from '../../legacy_uptime/lib/lib'; import { ProjectMonitor } from '../../../common/runtime_types'; @@ -42,7 +43,9 @@ export const addSyntheticsProjectMonitorRouteLegacy: SyntheticsStreamingRouteFac const monitors = (request.body?.monitors as ProjectMonitor[]) || []; try { - const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); + const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? { + id: DEFAULT_SPACE_ID, + }; const { keep_stale: keepStale, project: projectId } = request.body || {}; const { publicLocations, privateLocations } = await getAllLocations({ @@ -79,7 +82,7 @@ export const addSyntheticsProjectMonitorRouteLegacy: SyntheticsStreamingRouteFac }); } catch (error) { if (error?.output?.statusCode === 404) { - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; subject?.next(`Unable to create monitors. Kibana space '${spaceId}' does not exist.`); subject?.next({ failedMonitors: monitors.map((m) => m.id) }); } else { diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/bulk_cruds/delete_monitor_bulk.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/bulk_cruds/delete_monitor_bulk.ts index f5007986d250..90130ccaae2d 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/bulk_cruds/delete_monitor_bulk.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/bulk_cruds/delete_monitor_bulk.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsClientContract, KibanaRequest } from '@kbn/core/server'; import { SavedObject } from '@kbn/core-saved-objects-server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { formatTelemetryDeleteEvent, sendTelemetryEvents, @@ -37,7 +38,10 @@ export const deleteMonitorBulk = async ({ const { logger, telemetry, stackVersion } = server; try { - const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); + const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? { + id: DEFAULT_SPACE_ID, + }; + const deleteSyncPromise = syntheticsMonitorClient.deleteMonitors( monitors.map((normalizedMonitor) => ({ ...normalizedMonitor.attributes, diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts index 2f3b4b2595dc..2b194ff62d88 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts @@ -10,6 +10,7 @@ import { SavedObjectsClientContract, SavedObjectsErrorHelpers, } from '@kbn/core/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { deletePermissionError } from '../../synthetics_service/private_location/synthetics_private_location'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; import { @@ -108,7 +109,7 @@ export const deleteMonitor = async ({ } try { - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const deleteSyncPromise = syntheticsMonitorClient.deleteMonitors( [ { diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts index 09b046b872ed..90057f6ea7ef 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/edit_monitor.ts @@ -13,6 +13,7 @@ import { KibanaRequest, } from '@kbn/core/server'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { getSyntheticsPrivateLocations } from '../../legacy_uptime/lib/saved_objects/private_locations'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; import { @@ -57,7 +58,7 @@ export const editSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ( const { monitorId } = request.params; try { - const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const previousMonitor: SavedObject = await savedObjectsClient.get( syntheticsMonitorType, diff --git a/x-pack/plugins/synthetics/server/routes/settings/add_param.ts b/x-pack/plugins/synthetics/server/routes/settings/add_param.ts index adb97493a51d..a563ee83d2d5 100644 --- a/x-pack/plugins/synthetics/server/routes/settings/add_param.ts +++ b/x-pack/plugins/synthetics/server/routes/settings/add_param.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { SyntheticsParam } from '../../../common/runtime_types'; import { syntheticsParamType } from '../../../common/types/saved_objects'; import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types'; @@ -27,7 +28,7 @@ export const addSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({ writeAccess: true, handler: async ({ request, server, savedObjectsClient }): Promise => { const { namespaces, ...data } = request.body as SyntheticsParam; - const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const result = await savedObjectsClient.create(syntheticsParamType, data, { initialNamespaces: (namespaces ?? []).length > 0 ? namespaces : [spaceId], diff --git a/x-pack/plugins/synthetics/server/routes/settings/params.ts b/x-pack/plugins/synthetics/server/routes/settings/params.ts index c75b94186fb8..e713ca7a33ff 100644 --- a/x-pack/plugins/synthetics/server/routes/settings/params.ts +++ b/x-pack/plugins/synthetics/server/routes/settings/params.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsFindResult } from '@kbn/core-saved-objects-api-server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { syntheticsParamType } from '../../../common/types/saved_objects'; import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types'; import { SYNTHETICS_API_URLS } from '../../../common/constants'; @@ -17,7 +18,7 @@ export const getSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({ handler: async ({ savedObjectsClient, request, server }): Promise => { const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const canSave = (await server.coreStart?.capabilities.resolveCapabilities(request)).uptime.save ?? false; diff --git a/x-pack/plugins/synthetics/server/routes/settings/sync_global_params.ts b/x-pack/plugins/synthetics/server/routes/settings/sync_global_params.ts index d4b6c833e28e..7eb7d3035601 100644 --- a/x-pack/plugins/synthetics/server/routes/settings/sync_global_params.ts +++ b/x-pack/plugins/synthetics/server/routes/settings/sync_global_params.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { getPrivateLocations } from '../../synthetics_service/get_private_locations'; import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types'; import { SYNTHETICS_API_URLS } from '../../../common/constants'; @@ -20,7 +21,7 @@ export const syncParamsSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () request, server, }): Promise => { - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const allPrivateLocations = await getPrivateLocations( syntheticsMonitorClient, diff --git a/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts b/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts index b7cf5199bfd7..59ad3ed36a2d 100644 --- a/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/synthetics_service/run_once_monitor.ts @@ -5,6 +5,7 @@ * 2.0. */ import { schema } from '@kbn/config-schema'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { MonitorFields } from '../../../common/runtime_types'; import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types'; import { API_URLS } from '../../../common/constants'; @@ -26,7 +27,7 @@ export const runOnceSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = const validationResult = validateMonitor(monitor); - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; if (!validationResult.valid || !validationResult.decodedMonitor) { const { reason: message, details, payload } = validationResult; diff --git a/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts b/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts index e4cd6d22704c..cb6c8fb1c8bc 100644 --- a/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; import { v4 as uuidv4 } from 'uuid'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { TestNowResponse } from '../../../common/types'; import { ConfigKey, @@ -44,7 +45,7 @@ export const testNowMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ const testRunId = uuidv4(); - const spaceId = server.spaces.spacesService.getSpaceId(request); + const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; const paramsBySpace = await syntheticsService.getSyntheticsParams({ spaceId }); From 55afbba7c020da83f0e72858565eee0a07cb713c Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 28 Mar 2023 17:07:33 +0200 Subject: [PATCH 8/8] [Synthetics] Fix usage of synthetics-* index pattern the synthetics app (#152473) Co-authored-by: Dominique Clarke --- .../e2e/journeys/step_duration.journey.ts | 2 +- .../app/observability_status/content.ts | 2 +- .../components/app/section/uptime/index.tsx | 4 +- .../hooks/use_lens_attributes.test.tsx | 1 + .../shared/exploratory_view/labels.ts | 5 ++ .../obsv_exploratory_view.tsx | 10 +++ .../series_editor/columns/date_picker_col.tsx | 3 +- .../shared/exploratory_view/types.ts | 1 + .../observability/public/context/constants.ts | 1 + .../public/context/has_data_context.test.tsx | 30 +++---- .../public/context/has_data_context.tsx | 6 +- .../observability/public/data_handler.test.ts | 6 +- .../overview/components/empty_sections.tsx | 2 +- .../pages/overview/overview.stories.tsx | 18 ++-- .../typings/fetch_overview_data/index.ts | 6 +- .../observability_data_views.ts | 8 ++ .../common/constants/synthetics/rest_api.ts | 5 ++ .../journey_screenshot_dialog.test.tsx | 6 +- .../screenshot/journey_screenshot_dialog.tsx | 21 ++++- ...journey_step_screenshot_container.test.tsx | 5 +- .../journey_step_screenshot_container.tsx | 3 +- .../hooks/use_synthetics_priviliges.tsx | 79 +++++++++++++++++ .../public/apps/synthetics/routes.tsx | 7 +- .../synthetics/state/browser_journey/api.ts | 12 ++- .../synthetics/state/network_events/api.ts | 4 +- .../app/uptime_page_template.tsx | 4 +- .../common/header/action_menu_content.tsx | 2 +- .../monitor_duration/monitor_duration.tsx | 1 - .../monitor_duration_container.tsx | 24 ------ .../waterfall_marker_trend.test.tsx | 2 +- .../check_steps/step_field_trend.tsx | 2 +- x-pack/plugins/synthetics/public/plugin.ts | 2 +- .../server/legacy_uptime/lib/lib.ts | 71 +++++++++------- .../lib/requests/get_journey_screenshot.ts | 2 +- .../pings/journey_screenshot_blocks.test.ts | 39 ++++++++- .../routes/pings/journey_screenshot_blocks.ts | 41 +++++---- .../routes/pings/journey_screenshots.test.ts | 47 ++++++++++- .../routes/pings/journey_screenshots.ts | 67 ++++++++------- .../synthetics/last_successful_check.ts | 84 +++++++++++-------- .../server/legacy_uptime/routes/types.ts | 20 +++-- .../routes/uptime_route_wrapper.ts | 56 ++++++++----- .../plugins/synthetics/server/routes/index.ts | 11 ++- .../network_events/get_network_events.ts | 32 +++++++ .../server/routes/network_events/index.ts | 8 ++ .../routes/pings/journey_screenshot_blocks.ts | 27 ++++++ .../routes/pings/journey_screenshots.ts | 28 +++++++ .../server/routes/pings/journeys.ts | 16 ++-- .../routes/pings/last_successful_check.ts | 30 +++++++ .../server/synthetics_route_wrapper.ts | 75 ++++++++++------- .../authentication/check_has_privilege.ts | 17 +++- 50 files changed, 686 insertions(+), 269 deletions(-) create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_synthetics_priviliges.tsx create mode 100644 x-pack/plugins/synthetics/server/routes/network_events/get_network_events.ts create mode 100644 x-pack/plugins/synthetics/server/routes/network_events/index.ts create mode 100644 x-pack/plugins/synthetics/server/routes/pings/journey_screenshot_blocks.ts create mode 100644 x-pack/plugins/synthetics/server/routes/pings/journey_screenshots.ts create mode 100644 x-pack/plugins/synthetics/server/routes/pings/last_successful_check.ts diff --git a/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts b/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts index 52da0a786fdb..10cc98fa2da6 100644 --- a/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts +++ b/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts @@ -27,7 +27,7 @@ journey('Exploratory view', async ({ page, params }) => { reportType: 'kpi-over-time', allSeries: [ { - dataType: 'synthetics', + dataType: 'uptime', time: { from: moment().subtract(10, 'y').toISOString(), to: moment().toISOString(), diff --git a/x-pack/plugins/observability/public/components/app/observability_status/content.ts b/x-pack/plugins/observability/public/components/app/observability_status/content.ts index 319c701fd96c..3081ee2ebf29 100644 --- a/x-pack/plugins/observability/public/components/app/observability_status/content.ts +++ b/x-pack/plugins/observability/public/components/app/observability_status/content.ts @@ -86,7 +86,7 @@ export const getContent = ( weight: 2, }, { - id: 'synthetics', + id: 'uptime', title: i18n.translate('xpack.observability.statusVisualization.uptime.title', { defaultMessage: 'Uptime', }), diff --git a/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx b/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx index 8e76c1316be2..a7d482d6da2f 100644 --- a/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx @@ -53,7 +53,7 @@ export function UptimeSection({ bucketSize }: Props) { const { data, status } = useFetcher( () => { if (bucketSize && absoluteStart && absoluteEnd) { - return getDataHandler('synthetics')?.fetchData({ + return getDataHandler('uptime')?.fetchData({ absoluteTime: { start: absoluteStart, end: absoluteEnd }, relativeTime: { start: relativeStart, end: relativeEnd }, timeZone, @@ -75,7 +75,7 @@ export function UptimeSection({ bucketSize }: Props) { ] ); - if (!hasDataMap.synthetics?.hasData) { + if (!hasDataMap.uptime?.hasData) { return null; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx index 1080c1d51d28..d98a8a8260cb 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx @@ -46,6 +46,7 @@ describe('useExpViewTimeRange', function () { infra_logs: mockDataView, infra_metrics: mockDataView, synthetics: mockDataView, + uptime: mockDataView, alerts: mockDataView, }, }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/labels.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/labels.ts index 7f975c5777d4..add254e327d1 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/labels.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/labels.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; export enum DataTypes { SYNTHETICS = 'synthetics', + UPTIME = 'uptime', UX = 'ux', MOBILE = 'mobile', METRICS = 'infra_metrics', @@ -27,6 +28,10 @@ export const DataTypesLabels: Record = { } ), + [DataTypes.UPTIME]: i18n.translate('xpack.observability.overview.exploratoryView.uptimeLabel', { + defaultMessage: 'Uptime', + }), + [DataTypes.METRICS]: i18n.translate('xpack.observability.overview.exploratoryView.metricsLabel', { defaultMessage: 'Metrics', }), diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx index fb30cb173464..9b3f7b470dd3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx @@ -38,6 +38,10 @@ import { getLogsKPIConfig } from './configurations/infra_logs/kpi_over_time_conf import { getSingleMetricConfig } from './configurations/rum/single_metric_config'; export const dataTypes: Array<{ id: AppDataType; label: string }> = [ + { + id: DataTypes.UPTIME, + label: DataTypesLabels[DataTypes.UPTIME], + }, { id: DataTypes.SYNTHETICS, label: DataTypesLabels[DataTypes.SYNTHETICS], @@ -85,6 +89,12 @@ export const obsvReportConfigMap = { getSyntheticsSingleMetricConfig, getSyntheticsHeatmapConfig, ], + [DataTypes.UPTIME]: [ + getSyntheticsKPIConfig, + getSyntheticsDistributionConfig, + getSyntheticsSingleMetricConfig, + getSyntheticsHeatmapConfig, + ], [DataTypes.MOBILE]: [ getMobileKPIConfig, getMobileKPIDistributionConfig, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx index 7a45920cfd83..cc6c76755689 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx @@ -27,7 +27,8 @@ interface Props { const AddDataComponents: Record = { mobile: MobileAddData, ux: UXAddData, - synthetics: SyntheticsAddData, + uptime: SyntheticsAddData, + synthetics: null, apm: null, infra_logs: null, infra_metrics: null, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index 243aefafbc22..690f00e19fec 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -149,6 +149,7 @@ interface FormatType extends SerializedFieldFormat { export type AppDataType = | 'synthetics' + | 'uptime' | 'ux' | 'infra_logs' | 'infra_metrics' diff --git a/x-pack/plugins/observability/public/context/constants.ts b/x-pack/plugins/observability/public/context/constants.ts index 695febb81aff..962622b128de 100644 --- a/x-pack/plugins/observability/public/context/constants.ts +++ b/x-pack/plugins/observability/public/context/constants.ts @@ -8,6 +8,7 @@ export const ALERT_APP = 'alert'; export const UX_APP = 'ux'; export const SYNTHETICS_APP = 'synthetics'; +export const UPTIME_APP = 'uptime'; export const APM_APP = 'apm'; export const INFRA_LOGS_APP = 'infra_logs'; export const INFRA_METRICS_APP = 'infra_metrics'; diff --git a/x-pack/plugins/observability/public/context/has_data_context.test.tsx b/x-pack/plugins/observability/public/context/has_data_context.test.tsx index 1cac51968027..6d6a99e9b864 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.test.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.test.tsx @@ -37,7 +37,7 @@ function unregisterAll() { unregisterDataHandler({ appName: 'apm' }); unregisterDataHandler({ appName: 'infra_logs' }); unregisterDataHandler({ appName: 'infra_metrics' }); - unregisterDataHandler({ appName: 'synthetics' }); + unregisterDataHandler({ appName: 'uptime' }); unregisterDataHandler({ appName: 'ux' }); } @@ -73,7 +73,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: undefined, status: 'success' }, - synthetics: { hasData: undefined, status: 'success' }, + uptime: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, infra_metrics: { hasData: undefined, status: 'success' }, ux: { hasData: undefined, status: 'success' }, @@ -98,7 +98,7 @@ describe('HasDataContextProvider', () => { }, { appName: 'infra_metrics', hasData: async () => ({ hasData: false }) }, { - appName: 'synthetics', + appName: 'uptime', hasData: async () => ({ hasData: false }), }, { @@ -128,7 +128,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: false, status: 'success' }, - synthetics: { + uptime: { hasData: false, status: 'success', }, @@ -161,7 +161,7 @@ describe('HasDataContextProvider', () => { hasData: async () => ({ hasData: false, indices: 'metric-*' }), }, { - appName: 'synthetics', + appName: 'uptime', hasData: async () => ({ hasData: false, indices: 'heartbeat-*, synthetics-*' }), }, { @@ -190,7 +190,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: true, status: 'success' }, - synthetics: { + uptime: { hasData: false, indices: 'heartbeat-*, synthetics-*', status: 'success', @@ -225,7 +225,7 @@ describe('HasDataContextProvider', () => { hasData: async () => ({ hasData: true, indices: 'metric-*' }), }, { - appName: 'synthetics', + appName: 'uptime', hasData: async () => ({ hasData: true, indices: 'heartbeat-*, synthetics-*' }), }, { @@ -257,7 +257,7 @@ describe('HasDataContextProvider', () => { hasData: true, status: 'success', }, - synthetics: { + uptime: { hasData: true, indices: 'heartbeat-*, synthetics-*', status: 'success', @@ -309,7 +309,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: true, indices: sampleAPMIndices, status: 'success' }, - synthetics: { hasData: undefined, status: 'success' }, + uptime: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, infra_metrics: { hasData: undefined, status: 'success' }, ux: { hasData: undefined, status: 'success' }, @@ -358,7 +358,7 @@ describe('HasDataContextProvider', () => { indices: sampleAPMIndices, status: 'success', }, - synthetics: { hasData: undefined, status: 'success' }, + uptime: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, infra_metrics: { hasData: undefined, status: 'success' }, ux: { hasData: undefined, status: 'success' }, @@ -391,7 +391,7 @@ describe('HasDataContextProvider', () => { hasData: async () => ({ hasData: true, indices: 'metric-*' }), }, { - appName: 'synthetics', + appName: 'uptime', hasData: async () => ({ hasData: true, indices: 'heartbeat-*, synthetics-*' }), }, { @@ -420,7 +420,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: undefined, status: 'failure' }, - synthetics: { + uptime: { hasData: true, indices: 'heartbeat-*, synthetics-*', status: 'success', @@ -465,7 +465,7 @@ describe('HasDataContextProvider', () => { }, }, { - appName: 'synthetics', + appName: 'uptime', hasData: async () => { throw new Error('BOOMMMMM'); }, @@ -498,7 +498,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: undefined, status: 'failure' }, - synthetics: { hasData: undefined, status: 'failure' }, + uptime: { hasData: undefined, status: 'failure' }, infra_logs: { hasData: undefined, status: 'failure' }, infra_metrics: { hasData: undefined, status: 'failure' }, ux: { hasData: undefined, status: 'failure' }, @@ -544,7 +544,7 @@ describe('HasDataContextProvider', () => { expect(result.current).toEqual({ hasDataMap: { apm: { hasData: undefined, status: 'success' }, - synthetics: { hasData: undefined, status: 'success' }, + uptime: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, infra_metrics: { hasData: undefined, status: 'success' }, ux: { hasData: undefined, status: 'success' }, diff --git a/x-pack/plugins/observability/public/context/has_data_context.tsx b/x-pack/plugins/observability/public/context/has_data_context.tsx index 775b401b5086..dda9ca4ef37f 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.tsx @@ -15,7 +15,7 @@ import { APM_APP, INFRA_LOGS_APP, INFRA_METRICS_APP, - SYNTHETICS_APP, + UPTIME_APP, UX_APP, } from './constants'; import { getDataHandler } from '../data_handler'; @@ -50,7 +50,7 @@ export const HasDataContext = createContext({} as HasDataContextValue); const apps: DataContextApps[] = [ APM_APP, - SYNTHETICS_APP, + UPTIME_APP, INFRA_LOGS_APP, INFRA_METRICS_APP, UX_APP, @@ -100,7 +100,7 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode serviceName: resultUx?.serviceName as string, }); break; - case SYNTHETICS_APP: + case UPTIME_APP: const resultSy = await getDataHandler(app)?.hasData(); updateState({ hasData: resultSy?.hasData, indices: resultSy?.indices }); diff --git a/x-pack/plugins/observability/public/data_handler.test.ts b/x-pack/plugins/observability/public/data_handler.test.ts index d0cca3b5272e..736fd3d968bd 100644 --- a/x-pack/plugins/observability/public/data_handler.test.ts +++ b/x-pack/plugins/observability/public/data_handler.test.ts @@ -188,7 +188,7 @@ describe('registerDataHandler', () => { }); describe('Uptime', () => { registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: async () => { return { title: 'uptime', @@ -226,13 +226,13 @@ describe('registerDataHandler', () => { }); it('registered data handler', () => { - const dataHandler = getDataHandler('synthetics'); + const dataHandler = getDataHandler('uptime'); expect(dataHandler?.fetchData).toBeDefined(); expect(dataHandler?.hasData).toBeDefined(); }); it('returns data when fetchData is called', async () => { - const dataHandler = getDataHandler('synthetics'); + const dataHandler = getDataHandler('uptime'); const response = await dataHandler?.fetchData(params); expect(response).toEqual({ title: 'uptime', diff --git a/x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx b/x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx index 704d0c6432dd..bc91f20bf3b9 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx @@ -106,7 +106,7 @@ const getEmptySections = ({ http }: { http: HttpSetup }): ISection[] => { href: http.basePath.prepend('/app/home#/tutorial_directory/metrics'), }, { - id: 'synthetics', + id: 'uptime', title: i18n.translate('xpack.observability.emptySection.apps.uptime.title', { defaultMessage: 'Uptime', }), diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index b139564019e7..e17a52793380 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -31,7 +31,7 @@ function unregisterAll() { unregisterDataHandler({ appName: 'apm' }); unregisterDataHandler({ appName: 'infra_logs' }); unregisterDataHandler({ appName: 'infra_metrics' }); - unregisterDataHandler({ appName: 'synthetics' }); + unregisterDataHandler({ appName: 'uptime' }); } const sampleAPMIndices = { transaction: 'apm-*' } as ApmIndicesConfig; @@ -235,7 +235,7 @@ storiesOf('app/Overview', module) hasData: async () => ({ hasData: false, indices: 'metric-*' }), }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: fetchUptimeData, hasData: async () => ({ hasData: false, indices: 'heartbeat-*,synthetics-*' }), }); @@ -323,7 +323,7 @@ storiesOf('app/Overview', module) hasData: async () => ({ hasData: true, indices: 'metric-*' }), }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: fetchUptimeData, hasData: async () => ({ hasData: true, indices: 'heartbeat-*,synthetics-*' }), }); @@ -349,7 +349,7 @@ storiesOf('app/Overview', module) hasData: async () => ({ hasData: true, indices: 'metric-*' }), }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: fetchUptimeData, hasData: async () => ({ hasData: true, indices: 'heartbeat-*,synthetics-*' }), }); @@ -377,7 +377,7 @@ storiesOf('app/Overview', module) hasData: async () => ({ hasData: true, indices: 'metric-*' }), }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: fetchUptimeData, hasData: async () => ({ hasData: true, indices: 'heartbeat-*,synthetics-*' }), }); @@ -402,7 +402,7 @@ storiesOf('app/Overview', module) hasData: async () => ({ hasData: true, indices: 'metric-*' }), }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: async () => emptyUptimeResponse, hasData: async () => ({ hasData: true, indices: 'heartbeat-*,synthetics-*' }), }); @@ -434,7 +434,7 @@ storiesOf('app/Overview', module) hasData: async () => ({ hasData: true, indices: 'metric-*' }), }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: async () => { throw new Error('Error fetching Uptime data'); }, @@ -472,7 +472,7 @@ storiesOf('app/Overview', module) }, }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: fetchUptimeData, // @ts-ignore throws an error instead hasData: async () => { @@ -509,7 +509,7 @@ storiesOf('app/Overview', module) }, }); registerDataHandler({ - appName: 'synthetics', + appName: 'uptime', fetchData: fetchUptimeData, // @ts-ignore throws an error instead hasData: async () => { diff --git a/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts index 64a66dff66e4..78837ed27f80 100644 --- a/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts +++ b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts @@ -76,7 +76,7 @@ export type HasData = ( export type ObservabilityFetchDataPlugins = Exclude< ObservabilityApp, - 'observability-overview' | 'stack_monitoring' | 'uptime' | 'fleet' + 'observability-overview' | 'stack_monitoring' | 'fleet' | 'synthetics' >; export interface DataHandler< @@ -154,7 +154,7 @@ export interface ObservabilityFetchDataResponse { apm: ApmFetchDataResponse; infra_metrics: MetricsFetchDataResponse; infra_logs: LogsFetchDataResponse; - synthetics: UptimeFetchDataResponse; + uptime: UptimeFetchDataResponse; ux: UxFetchDataResponse; } @@ -162,6 +162,6 @@ export interface ObservabilityHasDataResponse { apm: APMHasDataResponse; infra_metrics: InfraMetricsHasDataResponse; infra_logs: InfraLogsHasDataResponse; - synthetics: SyntheticsHasDataResponse; + uptime: SyntheticsHasDataResponse; ux: UXHasDataResponse; } diff --git a/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts index 75851f87fa8d..f5e8893ce1b2 100644 --- a/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts +++ b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts @@ -33,6 +33,7 @@ const appFieldFormats: Record = { infra_metrics: infraMetricsFieldFormats, ux: rumFieldFormats, apm: apmFieldFormats, + uptime: syntheticsFieldFormats, synthetics: syntheticsFieldFormats, mobile: apmFieldFormats, alerts: null, @@ -43,6 +44,7 @@ const appRuntimeFields: Record = { synthetics: 'synthetics_static_index_pattern_id', + uptime: 'uptime_static_index_pattern_id', apm: 'apm_static_index_pattern_id', ux: 'rum_static_index_pattern_id', infra_logs: 'infra_logs_static_index_pattern_id', @@ -75,6 +78,11 @@ const getAppDataViewId = (app: AppDataType, indices: string) => { export async function getDataTypeIndices(dataType: AppDataType) { switch (dataType) { + case 'synthetics': + return { + hasData: true, + indices: 'synthetics-*', + }; case 'mobile': case 'ux': case 'apm': diff --git a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts index a0875d9df497..e310762c7914 100644 --- a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts +++ b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts @@ -16,4 +16,9 @@ export enum SYNTHETICS_API_URLS { SYNC_GLOBAL_PARAMS = `/synthetics/sync_global_params`, ENABLE_DEFAULT_ALERTING = `/synthetics/enable_default_alerting`, JOURNEY = `/internal/synthetics/journey/{checkGroup}`, + SYNTHETICS_SUCCESSFUL_CHECK = `/internal/synthetics/synthetics/check/success`, + JOURNEY_SCREENSHOT_BLOCKS = `/internal/synthetics/journey/screenshot/block`, + JOURNEY_FAILED_STEPS = `/internal/synthetics/journeys/failed_steps`, + NETWORK_EVENTS = `/internal/synthetics/network_events`, + JOURNEY_SCREENSHOT = `/internal/synthetics/journey/screenshot/{checkGroup}/{stepIndex}`, } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.test.tsx index eb180b8e362e..44e6db576d3f 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.test.tsx @@ -56,12 +56,11 @@ describe('JourneyScreenshotDialog', () => { expect(() => render()).not.toThrowError(); }); - it('shows loading indicator when image is loading', () => { + it('shows loading indicator when image is loading', async () => { const { queryByTestId } = render(); expect(queryByTestId('screenshotImageLoadingProgress')).not.toBeInTheDocument(); userEvent.click(queryByTestId('screenshotImageNextButton')); - expect(queryByTestId('screenshotImageLoadingProgress')).toBeInTheDocument(); }); it('respects maxSteps', () => { @@ -69,7 +68,6 @@ describe('JourneyScreenshotDialog', () => { expect(queryByTestId('screenshotImageLoadingProgress')).not.toBeInTheDocument(); userEvent.click(queryByTestId('screenshotImageNextButton')); - expect(queryByTestId('screenshotImageLoadingProgress')).toBeInTheDocument(); expect(queryByTestId('screenshotImageNextButton')).toHaveProperty('disabled'); }); @@ -79,6 +77,6 @@ describe('JourneyScreenshotDialog', () => { 'src', 'http://localhost/test-img-url-1' ); - expect(getByText('First step')).toBeInTheDocument(); + expect(getByText('Step: 1 of 1')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.tsx index dd3607e8221f..818541b1f73e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_screenshot_dialog.tsx @@ -28,6 +28,7 @@ import { useIsWithinMaxBreakpoint, } from '@elastic/eui'; +import { SYNTHETICS_API_URLS } from '../../../../../../common/constants'; import { SyntheticsSettingsContext } from '../../../contexts'; import { useRetrieveStepImage } from '../monitor_test_result/use_retrieve_step_image'; @@ -54,7 +55,7 @@ export const JourneyScreenshotDialog = ({ const [stepNumber, setStepNumber] = useState(initialStepNumber); const { basePath } = useContext(SyntheticsSettingsContext); - const imgPath = `${basePath}/internal/uptime/journey/screenshot/${checkGroup}/${stepNumber}`; + const imgPath = getScreenshotUrl({ basePath, checkGroup, stepNumber }); const imageResult = useRetrieveStepImage({ hasIntersected: true, @@ -205,6 +206,24 @@ export const JourneyScreenshotDialog = ({ ) : null; }; +export const getScreenshotUrl = ({ + basePath, + checkGroup, + stepNumber, +}: { + basePath: string; + checkGroup?: string; + stepNumber: number; +}) => { + if (!checkGroup) { + return ''; + } + return `${basePath}${SYNTHETICS_API_URLS.JOURNEY_SCREENSHOT.replace( + '{checkGroup}', + checkGroup + ).replace('{stepIndex}', stepNumber.toString())}`; +}; + export const formatScreenshotStepsCount = (stepNumber: number, totalSteps: number) => i18n.translate('xpack.synthetics.monitor.stepOfSteps', { defaultMessage: 'Step: {stepNumber} of {totalSteps}', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.test.tsx index fc194af26038..9f5e313b78dd 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.test.tsx @@ -11,13 +11,14 @@ import { JourneyStepScreenshotContainer } from './journey_step_screenshot_contai import { render } from '../../../utils/testing'; import * as observabilityPublic from '@kbn/observability-plugin/public'; import * as retrieveHooks from '../monitor_test_result/use_retrieve_step_image'; +import { getScreenshotUrl } from './journey_screenshot_dialog'; jest.mock('@kbn/observability-plugin/public'); jest.setTimeout(10 * 1000); -const imgPath1 = '/internal/uptime/journey/screenshot/test-check-group/1'; -const imgPath2 = '/internal/uptime/journey/screenshot/test-check-group/2'; +const imgPath1 = getScreenshotUrl({ basePath: '', checkGroup: 'test-check-group', stepNumber: 1 }); +const imgPath2 = getScreenshotUrl({ basePath: '', checkGroup: 'test-check-group', stepNumber: 2 }); const testImageDataResult = { [imgPath1]: { attempts: 1, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.tsx index e7f7d05e1fcf..f4fd7470545e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/screenshot/journey_step_screenshot_container.tsx @@ -8,6 +8,7 @@ import React, { useContext } from 'react'; import useIntersection from 'react-use/lib/useIntersection'; +import { getScreenshotUrl } from './journey_screenshot_dialog'; import { SyntheticsSettingsContext } from '../../../contexts'; import { useRetrieveStepImage } from '../monitor_test_result/use_retrieve_step_image'; @@ -42,7 +43,7 @@ export const JourneyStepScreenshotContainer = ({ const { basePath } = useContext(SyntheticsSettingsContext); const imgPath = checkGroup - ? `${basePath}/internal/uptime/journey/screenshot/${checkGroup}/${initialStepNumber}` + ? getScreenshotUrl({ basePath, checkGroup, stepNumber: initialStepNumber }) : ''; const intersection = useIntersection(intersectionRef, { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_synthetics_priviliges.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_synthetics_priviliges.tsx new file mode 100644 index 000000000000..b1f1d57a33fd --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_synthetics_priviliges.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useSelector } from 'react-redux'; +import React from 'react'; +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiMarkdownFormat, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import { selectOverviewStatus } from '../state/overview_status'; +import { SYNTHETICS_INDEX_PATTERN } from '../../../../common/constants'; + +export const useSyntheticsPrivileges = () => { + const { error } = useSelector(selectOverviewStatus); + + if (error?.body?.message?.startsWith('MissingIndicesPrivileges:')) { + return ( + + + + + + ); + } +}; + +const Unprivileged = ({ unprivilegedIndices }: { unprivilegedIndices: string[] }) => ( + } + title={ +

+ +

+ } + body={ +

+ +

+ } + footer={ + `\n- \`${idx}\``) + } + /> + } + /> +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx index 413680f14d60..d60c4c25d95a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx @@ -18,6 +18,7 @@ import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { LazyObservabilityPageTemplateProps } from '@kbn/observability-plugin/public'; import { useInspectorContext } from '@kbn/observability-plugin/public'; +import { useSyntheticsPrivileges } from './hooks/use_synthetics_priviliges'; import { ClientPluginsStart } from '../../plugin'; import { getMonitorsRoute } from './components/monitors_page/route_config'; import { getMonitorDetailsRoute } from './components/monitor_details/route_config'; @@ -192,6 +193,8 @@ export const PageRouter: FC = () => { apiService.addInspectorRequest = addInspectorRequest; + const isUnPrivileged = useSyntheticsPrivileges(); + return ( {routes.map( @@ -207,12 +210,12 @@ export const PageRouter: FC = () => {
- + {isUnPrivileged || }
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/browser_journey/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/browser_journey/api.ts index 33ed12db4b5d..b4b352d38876 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/browser_journey/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/browser_journey/api.ts @@ -17,7 +17,7 @@ import { Ping, PingType, } from '../../../../../common/runtime_types'; -import { API_URLS, SYNTHETICS_API_URLS } from '../../../../../common/constants'; +import { SYNTHETICS_API_URLS } from '../../../../../common/constants'; export interface FetchJourneyStepsParams { checkGroup: string; @@ -25,7 +25,7 @@ export interface FetchJourneyStepsParams { } export async function fetchScreenshotBlockSet(params: string[]): Promise { - return apiService.post(API_URLS.JOURNEY_SCREENSHOT_BLOCKS, { + return apiService.post(SYNTHETICS_API_URLS.JOURNEY_SCREENSHOT_BLOCKS, { hashes: params, }); } @@ -45,7 +45,11 @@ export async function fetchJourneysFailedSteps({ }: { checkGroups: string[]; }): Promise { - return apiService.get(API_URLS.JOURNEY_FAILED_STEPS, { checkGroups }, FailedStepsApiResponseType); + return apiService.get( + SYNTHETICS_API_URLS.JOURNEY_FAILED_STEPS, + { checkGroups }, + FailedStepsApiResponseType + ); } export async function fetchLastSuccessfulCheck({ @@ -60,7 +64,7 @@ export async function fetchLastSuccessfulCheck({ location?: string; }): Promise { return await apiService.get( - API_URLS.SYNTHETICS_SUCCESSFUL_CHECK, + SYNTHETICS_API_URLS.SYNTHETICS_SUCCESSFUL_CHECK, { monitorId, timestamp, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/network_events/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/network_events/api.ts index 8c52ebba47da..7bc509aaacb0 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/network_events/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/network_events/api.ts @@ -9,7 +9,7 @@ import { SyntheticsNetworkEventsApiResponse, SyntheticsNetworkEventsApiResponseType, } from '../../../../../common/runtime_types'; -import { API_URLS } from '../../../../../common/constants'; +import { SYNTHETICS_API_URLS } from '../../../../../common/constants'; import { apiService } from '../../../../utils/api_service'; import { FetchNetworkEventsParams } from './actions'; @@ -17,7 +17,7 @@ export async function fetchNetworkEvents( params: FetchNetworkEventsParams ): Promise { return (await apiService.get( - API_URLS.NETWORK_EVENTS, + SYNTHETICS_API_URLS.NETWORK_EVENTS, { checkGroup: params.checkGroup, stepIndex: params.stepIndex, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx index 9e3d0d6ecd38..c1003f969586 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx @@ -9,7 +9,7 @@ import React, { useEffect } from 'react'; import { EuiPageHeaderProps, EuiPageTemplateProps } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useInspectorContext } from '@kbn/observability-plugin/public'; -import { CERTIFICATES_ROUTE, OVERVIEW_ROUTE } from '../../../common/constants'; +import { CERTIFICATES_ROUTE, OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../../../common/constants'; import { ClientPluginsStart } from '../../plugin'; import { useNoDataConfig } from './use_no_data_config'; import { EmptyStateLoading } from '../components/overview/empty_state/empty_state_loading'; @@ -42,7 +42,7 @@ export const UptimePageTemplateComponent: React.FC inspectorAdapters.requests.reset(); }, [inspectorAdapters.requests]); - if (error) { + if (error && path !== SETTINGS_ROUTE) { return ; } diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx index a0c7f68d6b67..ad5551dfc00b 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx @@ -52,7 +52,7 @@ export function ActionMenuContent(): React.ReactElement { reportType: 'kpi-over-time', allSeries: [ { - dataType: 'synthetics', + dataType: 'uptime', seriesType: 'area', selectedMetricField: 'monitor.duration.us', time: { from: dateRangeStart, to: dateRangeEnd }, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx index 822a476f5225..562cfd803009 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx @@ -18,7 +18,6 @@ interface DurationChartProps { hasMLJob: boolean; anomalies: AnomalyRecords | null; locationDurationLines: LocationDurationLine[]; - exploratoryViewLink: string; } /** diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx index 32bf47451f95..a6fce56d9269 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx @@ -8,7 +8,6 @@ import React, { useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { JobStat } from '@kbn/ml-plugin/public'; -import { createExploratoryViewUrl } from '@kbn/observability-plugin/public'; import { useGetUrlParams } from '../../../hooks'; import { getAnomalyRecordsAction, @@ -25,7 +24,6 @@ import { UptimeRefreshContext } from '../../../contexts'; import { MonitorDurationComponent } from './monitor_duration'; import { MonitorIdParam } from '../../../../../common/types'; import { getMLJobId } from '../../../../../common/lib'; -import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; export const MonitorDuration: React.FC = ({ monitorId }) => { const { dateRangeStart, dateRangeEnd, absoluteDateRangeStart, absoluteDateRangeEnd } = @@ -47,27 +45,6 @@ export const MonitorDuration: React.FC = ({ monitorId }) => { const { lastRefresh } = useContext(UptimeRefreshContext); - const { basePath } = useUptimeSettingsContext(); - - const exploratoryViewLink = createExploratoryViewUrl( - { - reportType: 'kpi-over-time', - allSeries: [ - { - name: `${monitorId}-response-duration`, - time: { from: dateRangeStart, to: dateRangeEnd }, - reportDefinitions: { - 'monitor.id': [monitorId] as string[], - }, - breakdown: 'observer.geo.name', - operationType: 'average', - dataType: 'synthetics', - }, - ], - }, - basePath - ); - useEffect(() => { if (isMLAvailable && hasMLJob) { const anomalyParams = { @@ -96,7 +73,6 @@ export const MonitorDuration: React.FC = ({ monitorId }) => { anomalies={anomalies} hasMLJob={hasMLJob} loading={loading || jobsLoading} - exploratoryViewLink={exploratoryViewLink} locationDurationLines={durationLines?.locationDurationLines ?? []} /> ); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx index aeca7940ef77..0813108d1d1f 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx @@ -98,7 +98,7 @@ describe('', () => { selectedMetricField: 'field', time: { to: '2021-12-03T14:35:41.072Z', from: '2021-12-03T13:47:41.072Z' }, seriesType: 'area', - dataType: 'synthetics', + dataType: 'uptime', reportDefinitions: { 'monitor.name': [null], 'synthetics.step.name.keyword': ['test-name'], diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx index f337a9ba0bed..4968897941e7 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx @@ -51,7 +51,7 @@ export function StepFieldTrend({ selectedMetricField: field, time: getLast48Intervals(activeStep), seriesType: 'area', - dataType: 'synthetics', + dataType: 'uptime', reportDefinitions: { 'monitor.name': [activeStep.monitor.name!], 'synthetics.step.name.keyword': [activeStep.synthetics.step?.name!], diff --git a/x-pack/plugins/synthetics/public/plugin.ts b/x-pack/plugins/synthetics/public/plugin.ts index 2ac4aee91d9d..77de03480be7 100644 --- a/x-pack/plugins/synthetics/public/plugin.ts +++ b/x-pack/plugins/synthetics/public/plugin.ts @@ -132,7 +132,7 @@ export class UptimePlugin plugins.share.url.locators.create(editMonitorNavigatorParams); plugins.observability.dashboard.register({ - appName: 'synthetics', + appName: 'uptime', hasData: async () => { const dataHelper = await getUptimeDataHelper(); const status = await dataHelper.indexStatus(); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts index c77f75dae0ea..cf76c5abc978 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts @@ -51,7 +51,7 @@ export class UptimeEsClient { request?: KibanaRequest; baseESClient: ElasticsearchClient; heartbeatIndices: string; - isInspectorEnabled: boolean | undefined; + isInspectorEnabled?: Promise; inspectableEsQueries: InspectResponse = []; uiSettings?: CoreRequestHandlerContext['uiSettings']; savedObjectsClient: SavedObjectsClientContract; @@ -59,31 +59,28 @@ export class UptimeEsClient { constructor( savedObjectsClient: SavedObjectsClientContract, esClient: ElasticsearchClient, - isDev: boolean = false, - uiSettings?: CoreRequestHandlerContext['uiSettings'], - request?: KibanaRequest + options?: { + isDev?: boolean; + uiSettings?: CoreRequestHandlerContext['uiSettings']; + request?: KibanaRequest; + heartbeatIndices?: string; + } ) { + const { isDev = false, uiSettings, request, heartbeatIndices = '' } = options ?? {}; this.uiSettings = uiSettings; this.baseESClient = esClient; - this.isInspectorEnabled = undefined; this.savedObjectsClient = savedObjectsClient; this.request = request; - this.heartbeatIndices = ''; + this.heartbeatIndices = heartbeatIndices; this.isDev = isDev; this.inspectableEsQueries = []; + this.getInspectEnabled(); } async initSettings() { const self = this; - if (!self.heartbeatIndices) { - const [isInspectorEnabled, dynamicSettings] = await Promise.all([ - getInspectEnabled(self.uiSettings), - savedObjectsAdapter.getUptimeDynamicSettings(self.savedObjectsClient), - ]); - - self.heartbeatIndices = dynamicSettings?.heartbeatIndices || ''; - self.isInspectorEnabled = isInspectorEnabled; - } + const heartbeatIndices = await this.getIndices(); + self.heartbeatIndices = heartbeatIndices || ''; } async search( @@ -123,8 +120,8 @@ export class UptimeEsClient { }) ); } - - if (this.isInspectorEnabled && this.request) { + const isInspectorEnabled = await this.getInspectEnabled(); + if (isInspectorEnabled && this.request) { debugESCall({ startTime, request: this.request, @@ -155,7 +152,9 @@ export class UptimeEsClient { esError = e; } - if (this.isInspectorEnabled && this.request) { + const isInspectorEnabled = await this.getInspectEnabled(); + + if (isInspectorEnabled && this.request) { debugESCall({ startTime, request: this.request, @@ -175,27 +174,39 @@ export class UptimeEsClient { return this.savedObjectsClient; } - getInspectData(path: string) { - const isInspectorEnabled = - (this.isInspectorEnabled || this.isDev) && path !== API_URLS.DYNAMIC_SETTINGS; + async getInspectData(path: string) { + const isInspectorEnabled = await this.getInspectEnabled(); + const showInspectData = + (isInspectorEnabled || this.isDev) && path !== API_URLS.DYNAMIC_SETTINGS; - if (isInspectorEnabled) { + if (showInspectData) { return { _inspect: this.inspectableEsQueries }; } return {}; } -} + async getInspectEnabled() { + if (this.isInspectorEnabled !== undefined) { + return this.isInspectorEnabled; + } -export function createEsParams(params: T): T { - return params; -} + if (!this.uiSettings) { + return false; + } + + this.isInspectorEnabled = this.uiSettings.client.get(enableInspectEsQueries); + } -function getInspectEnabled(uiSettings?: CoreRequestHandlerContext['uiSettings']) { - if (!uiSettings) { - return false; + async getIndices() { + if (this.heartbeatIndices) { + return this.heartbeatIndices; + } + const settings = await savedObjectsAdapter.getUptimeDynamicSettings(this.savedObjectsClient); + return settings?.heartbeatIndices || ''; } +} - return uiSettings.client.get(enableInspectEsQueries); +export function createEsParams(params: T): T { + return params; } /* eslint-disable no-console */ diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_journey_screenshot.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_journey_screenshot.ts index 083f2562586c..a7cbc0cb50d3 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_journey_screenshot.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_journey_screenshot.ts @@ -63,7 +63,7 @@ export const getJourneyScreenshot: UMElasticsearchQueryFn< const screenshotsOrRefs = (result.body.aggregations?.step.image.hits.hits as ResultType[]) ?? null; - if (screenshotsOrRefs.length === 0) return null; + if (!screenshotsOrRefs || screenshotsOrRefs?.length === 0) return null; return { ...screenshotsOrRefs[0]._source, diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.test.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.test.ts index e865ad571edb..93d23932c3ac 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.test.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.test.ts @@ -9,11 +9,20 @@ import { createJourneyScreenshotBlocksRoute } from './journey_screenshot_blocks' import { UMServerLibs } from '../../uptime_server'; describe('journey screenshot blocks route', () => { - let handlerContext: unknown; + let handlerContext: any; let libs: UMServerLibs; + const data: any = []; beforeEach(() => { handlerContext = { - uptimeEsClient: jest.fn(), + uptimeEsClient: { + search: jest.fn().mockResolvedValue({ + body: { + hits: { + hits: data, + }, + }, + }), + }, request: { body: { hashes: ['hash1', 'hash2'], @@ -49,6 +58,32 @@ describe('journey screenshot blocks route', () => { }); it('returns blocks for request', async () => { + handlerContext.uptimeEsClient.search = jest.fn().mockResolvedValue({ + body: { + hits: { + hits: [ + { + _id: 'hash1', + _source: { + synthetics: { + blob: 'blob1', + blob_mime: 'image/jpeg', + }, + }, + }, + { + _id: 'hash2', + _source: { + synthetics: { + blob: 'blob2', + blob_mime: 'image/jpeg', + }, + }, + }, + ], + }, + }, + }); const responseData = [ { id: 'hash1', diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.ts index 83e77fd8c8bd..483444e62b1f 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshot_blocks.ts @@ -8,8 +8,9 @@ import * as t from 'io-ts'; import { isRight } from 'fp-ts/lib/Either'; import { schema } from '@kbn/config-schema'; +import { getJourneyScreenshotBlocks } from '../../lib/requests/get_journey_screenshot_blocks'; import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteFactory } from '../types'; +import { RouteContext, UMRestApiRouteFactory, UptimeRouteContext } from '../types'; import { API_URLS } from '../../../../common/constants'; function isStringArray(data: unknown): data is string[] { @@ -24,22 +25,30 @@ export const createJourneyScreenshotBlocksRoute: UMRestApiRouteFactory = (libs: hashes: schema.arrayOf(schema.string()), }), }, - handler: async ({ request, response, uptimeEsClient }) => { - const { hashes: blockIds } = request.body; + handler: async (routeProps) => { + return await journeyScreenshotBlocksHandler(routeProps); + }, +}); - if (!isStringArray(blockIds)) return response.badRequest(); +export const journeyScreenshotBlocksHandler = async ({ + response, + request, + uptimeEsClient, +}: RouteContext | UptimeRouteContext) => { + const { hashes: blockIds } = request.body; - const result = await libs.requests.getJourneyScreenshotBlocks({ - blockIds, - uptimeEsClient, - }); + if (!isStringArray(blockIds)) return response.badRequest(); - if (result.length === 0) { - return response.notFound(); - } + const result = await getJourneyScreenshotBlocks({ + blockIds, + uptimeEsClient, + }); - return response.ok({ - body: result, - }); - }, -}); + if (result.length === 0) { + return response.notFound(); + } + + return response.ok({ + body: result, + }); +}; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.test.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.test.ts index 4e0f5fc616f8..7c4a7d61173f 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.test.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.test.ts @@ -12,7 +12,15 @@ describe('journey screenshot route', () => { let handlerContext: any; beforeEach(() => { handlerContext = { - uptimeEsClient: jest.fn(), + uptimeEsClient: { + search: jest.fn().mockResolvedValue({ + body: { + hits: { + hits: [], + }, + }, + }), + }, request: { params: { checkGroup: 'check_group', @@ -63,6 +71,18 @@ describe('journey screenshot route', () => { totalSteps: 3, }; + handlerContext.uptimeEsClient.search = jest.fn().mockResolvedValue({ + body: { + hits: { + total: { + value: 3, + }, + hits: [], + }, + aggregations: { step: { image: { hits: { hits: [{ _source: mock }] } } } }, + }, + }); + const route = createJourneyScreenshotRoute({ requests: { getJourneyScreenshot: jest.fn().mockReturnValue(mock), @@ -93,8 +113,20 @@ describe('journey screenshot route', () => { }, type: 'step/screenshot', }, - totalSteps: 3, }; + + handlerContext.uptimeEsClient.search = jest.fn().mockResolvedValue({ + body: { + hits: { + total: { + value: 3, + }, + hits: [], + }, + aggregations: { step: { image: { hits: { hits: [{ _source: mock }] } } } }, + }, + }); + const route = createJourneyScreenshotRoute({ requests: { getJourneyScreenshot: jest.fn().mockReturnValue(mock), @@ -133,6 +165,17 @@ describe('journey screenshot route', () => { type: 'step/screenshot', }, }; + handlerContext.uptimeEsClient.search = jest.fn().mockResolvedValue({ + body: { + hits: { + total: { + value: 3, + }, + hits: [], + }, + aggregations: { step: { image: { hits: { hits: [{ _source: mock }] } } } }, + }, + }); const route = createJourneyScreenshotRoute({ requests: { getJourneyScreenshot: jest.fn().mockReturnValue(mock), diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.ts index 6ae3ae1b4566..7c56b3a26fa0 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/pings/journey_screenshots.ts @@ -8,8 +8,11 @@ import { schema } from '@kbn/config-schema'; import { isRefResult, isFullScreenshot } from '../../../../common/runtime_types/ping/synthetics'; import { UMServerLibs } from '../../lib/lib'; -import { ScreenshotReturnTypesUnion } from '../../lib/requests/get_journey_screenshot'; -import { UMRestApiRouteFactory } from '../types'; +import { + getJourneyScreenshot, + ScreenshotReturnTypesUnion, +} from '../../lib/requests/get_journey_screenshot'; +import { RouteContext, UMRestApiRouteFactory, UptimeRouteContext } from '../types'; import { API_URLS } from '../../../../common/constants'; function getSharedHeaders(stepName: string, totalSteps: number) { @@ -29,32 +32,40 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ stepIndex: schema.number(), }), }, - handler: async ({ uptimeEsClient, request, response }) => { - const { checkGroup, stepIndex } = request.params; + handler: async (routeProps) => { + return await journeyScreenshotHandler(routeProps); + }, +}); - const result: ScreenshotReturnTypesUnion | null = await libs.requests.getJourneyScreenshot({ - uptimeEsClient, - checkGroup, - stepIndex, - }); +export const journeyScreenshotHandler = async ({ + response, + request, + uptimeEsClient, +}: RouteContext | UptimeRouteContext) => { + const { checkGroup, stepIndex } = request.params; - if (isFullScreenshot(result) && typeof result.synthetics?.blob !== 'undefined') { - return response.ok({ - body: Buffer.from(result.synthetics.blob, 'base64'), - headers: { - 'content-type': result.synthetics.blob_mime || 'image/png', // falls back to 'image/png' for earlier versions of synthetics - ...getSharedHeaders(result.synthetics.step.name, result.totalSteps), - }, - }); - } else if (isRefResult(result)) { - return response.ok({ - body: { - screenshotRef: result, - }, - headers: getSharedHeaders(result.synthetics.step.name, result.totalSteps), - }); - } + const result: ScreenshotReturnTypesUnion | null = await getJourneyScreenshot({ + uptimeEsClient, + checkGroup, + stepIndex, + }); - return response.notFound(); - }, -}); + if (isFullScreenshot(result) && typeof result.synthetics?.blob !== 'undefined') { + return response.ok({ + body: Buffer.from(result.synthetics.blob, 'base64'), + headers: { + 'content-type': result.synthetics.blob_mime || 'image/png', // falls back to 'image/png' for earlier versions of synthetics + ...getSharedHeaders(result.synthetics.step.name, result.totalSteps), + }, + }); + } else if (isRefResult(result)) { + return response.ok({ + body: { + screenshotRef: result, + }, + headers: getSharedHeaders(result.synthetics.step.name, result.totalSteps), + }); + } + + return response.notFound(); +}; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/synthetics/last_successful_check.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/synthetics/last_successful_check.ts index 3edba46aa39e..5b1cb0fd33bc 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/synthetics/last_successful_check.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/synthetics/last_successful_check.ts @@ -6,11 +6,13 @@ */ import { schema } from '@kbn/config-schema'; +import { getJourneyScreenshot } from '../../lib/requests/get_journey_screenshot'; import { isRefResult, isFullScreenshot } from '../../../../common/runtime_types/ping/synthetics'; import { Ping } from '../../../../common/runtime_types/ping/ping'; import { UMServerLibs } from '../../lib/lib'; -import { UMRestApiRouteFactory } from '../types'; +import { RouteContext, UMRestApiRouteFactory, UptimeRouteContext } from '../types'; import { API_URLS } from '../../../../common/constants'; +import { getLastSuccessfulCheck } from '../../lib/requests/get_last_successful_check'; export const createLastSuccessfulCheckRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', @@ -23,41 +25,49 @@ export const createLastSuccessfulCheckRoute: UMRestApiRouteFactory = (libs: UMSe location: schema.maybe(schema.string()), }), }, - handler: async ({ uptimeEsClient, request, response }) => { - const { timestamp, monitorId, stepIndex, location } = request.query; - - const check: Ping | null = await libs.requests.getLastSuccessfulCheck({ - uptimeEsClient, - monitorId, - timestamp, - location, - }); - - if (check === null) { - return response.notFound(); - } - - if (!check.monitor.check_group) { - return response.ok({ body: check }); - } - - const screenshot = await libs.requests.getJourneyScreenshot({ - uptimeEsClient, - checkGroup: check.monitor.check_group, - stepIndex, - }); - - if (screenshot === null) { - return response.ok({ body: check }); - } - - if (check.synthetics) { - check.synthetics.isScreenshotRef = isRefResult(screenshot); - check.synthetics.isFullScreenshot = isFullScreenshot(screenshot); - } - - return response.ok({ - body: check, - }); + handler: async (routeProps) => { + return await getLastSuccessfulCheckScreenshot(routeProps); }, }); + +export const getLastSuccessfulCheckScreenshot = async ({ + response, + request, + uptimeEsClient, +}: RouteContext | UptimeRouteContext) => { + const { timestamp, monitorId, stepIndex, location } = request.query; + + const check: Ping | null = await getLastSuccessfulCheck({ + uptimeEsClient, + monitorId, + timestamp, + location, + }); + + if (check === null) { + return response.notFound(); + } + + if (!check.monitor.check_group) { + return response.ok({ body: check }); + } + + const screenshot = await getJourneyScreenshot({ + uptimeEsClient, + checkGroup: check.monitor.check_group, + stepIndex, + }); + + if (screenshot === null) { + return response.ok({ body: check }); + } + + if (check.synthetics) { + check.synthetics.isScreenshotRef = isRefResult(screenshot); + check.synthetics.isFullScreenshot = isFullScreenshot(screenshot); + } + + return response.ok({ + body: check, + }); +}; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts index 55286f8ea770..35ab8e9217eb 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts @@ -88,6 +88,16 @@ export type SyntheticsRouteWrapper = ( syntheticsMonitorClient: SyntheticsMonitorClient ) => UMKibanaRoute; +export interface UptimeRouteContext { + uptimeEsClient: UptimeEsClient; + context: UptimeRequestHandlerContext; + request: SyntheticsRequest; + response: KibanaResponseFactory; + savedObjectsClient: SavedObjectsClientContract; + server: UptimeServerSetup; + subject?: Subject; +} + /** * This is the contract we specify internally for route handling. */ @@ -99,15 +109,7 @@ export type UMRouteHandler = ({ server, savedObjectsClient, subject, -}: { - uptimeEsClient: UptimeEsClient; - context: UptimeRequestHandlerContext; - request: SyntheticsRequest; - response: KibanaResponseFactory; - savedObjectsClient: SavedObjectsClientContract; - server: UptimeServerSetup; - subject?: Subject; -}) => IKibanaResponse | Promise>; +}: UptimeRouteContext) => IKibanaResponse | Promise>; export interface RouteContext { uptimeEsClient: UptimeEsClient; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts index b5a025c885dc..97c204e90ac9 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts @@ -6,6 +6,7 @@ */ import { KibanaResponse } from '@kbn/core-http-router-server-internal'; +import { checkIndicesReadPrivileges } from '../../synthetics_service/authentication/check_has_privilege'; import { UMKibanaRouteWrapper } from './types'; import { isTestUser, UptimeEsClient } from '../lib/lib'; @@ -23,31 +24,46 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute, server) => const uptimeEsClient = new UptimeEsClient( coreContext.savedObjects.client, esClient.asCurrentUser, - Boolean(server.isDev && !isTestUser(server)), - coreContext.uiSettings, - request + { + request, + uiSettings: coreContext.uiSettings, + isDev: Boolean(server.isDev && !isTestUser(server)), + } ); server.uptimeEsClient = uptimeEsClient; + try { + const res = await uptimeRoute.handler({ + uptimeEsClient, + savedObjectsClient: coreContext.savedObjects.client, + context, + request, + response, + server, + }); - const res = await uptimeRoute.handler({ - uptimeEsClient, - savedObjectsClient: coreContext.savedObjects.client, - context, - request, - response, - server, - }); + if (res instanceof KibanaResponse) { + return res; + } - if (res instanceof KibanaResponse) { - return res; + return response.ok({ + body: { + ...res, + ...(await uptimeEsClient.getInspectData(uptimeRoute.path)), + }, + }); + } catch (e) { + if (e.statusCode === 403) { + const privileges = await checkIndicesReadPrivileges(uptimeEsClient); + if (!privileges.has_all_requested) { + return response.forbidden({ + body: { + message: `MissingIndicesPrivileges: You do not have permission to read from the ${uptimeEsClient.heartbeatIndices} indices. Please contact your administrator.`, + }, + }); + } + } + throw e; } - - return response.ok({ - body: { - ...res, - ...uptimeEsClient.getInspectData(uptimeRoute.path), - }, - }); }, }); diff --git a/x-pack/plugins/synthetics/server/routes/index.ts b/x-pack/plugins/synthetics/server/routes/index.ts index b394c53f2014..b77d1d13247e 100644 --- a/x-pack/plugins/synthetics/server/routes/index.ts +++ b/x-pack/plugins/synthetics/server/routes/index.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { createJourneyRoute } from './pings/journeys'; +import { createJourneyScreenshotRoute } from './pings/journey_screenshots'; +import { createJourneyScreenshotBlocksRoute } from './pings/journey_screenshot_blocks'; +import { createLastSuccessfulCheckRoute } from './pings/last_successful_check'; +import { createJourneyFailedStepsRoute, createJourneyRoute } from './pings/journeys'; import { updateDefaultAlertingRoute } from './default_alerts/update_default_alert'; import { syncParamsSyntheticsParamsRoute } from './settings/sync_global_params'; import { editSyntheticsParamsRoute } from './settings/edit_param'; @@ -44,6 +47,7 @@ import { getHasIntegrationMonitorsRoute } from './fleet/get_has_integration_moni import { addSyntheticsParamsRoute } from './settings/add_param'; import { enableDefaultAlertingRoute } from './default_alerts/enable_default_alert'; import { getDefaultAlertingRoute } from './default_alerts/get_default_alert'; +import { createNetworkEventsRoute } from './network_events'; import { addPrivateLocationRoute } from './settings/private_locations/add_private_location'; import { deletePrivateLocationRoute } from './settings/private_locations/delete_private_location'; import { getPrivateLocationsRoute } from './settings/private_locations/get_private_locations'; @@ -80,6 +84,11 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [ getDefaultAlertingRoute, updateDefaultAlertingRoute, createJourneyRoute, + createLastSuccessfulCheckRoute, + createJourneyScreenshotBlocksRoute, + createJourneyFailedStepsRoute, + createNetworkEventsRoute, + createJourneyScreenshotRoute, addPrivateLocationRoute, deletePrivateLocationRoute, getPrivateLocationsRoute, diff --git a/x-pack/plugins/synthetics/server/routes/network_events/get_network_events.ts b/x-pack/plugins/synthetics/server/routes/network_events/get_network_events.ts new file mode 100644 index 000000000000..114cb88d508b --- /dev/null +++ b/x-pack/plugins/synthetics/server/routes/network_events/get_network_events.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { getNetworkEvents } from '../../legacy_uptime/lib/requests/get_network_events'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; +import { UMServerLibs } from '../../legacy_uptime/uptime_server'; +import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; + +export const createNetworkEventsRoute: SyntheticsRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: SYNTHETICS_API_URLS.NETWORK_EVENTS, + validate: { + query: schema.object({ + checkGroup: schema.string(), + stepIndex: schema.number(), + }), + }, + handler: async ({ uptimeEsClient, request }): Promise => { + const { checkGroup, stepIndex } = request.query; + + return await getNetworkEvents({ + uptimeEsClient, + checkGroup, + stepIndex, + }); + }, +}); diff --git a/x-pack/plugins/synthetics/server/routes/network_events/index.ts b/x-pack/plugins/synthetics/server/routes/network_events/index.ts new file mode 100644 index 000000000000..e2b8c871e17b --- /dev/null +++ b/x-pack/plugins/synthetics/server/routes/network_events/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { createNetworkEventsRoute } from './get_network_events'; diff --git a/x-pack/plugins/synthetics/server/routes/pings/journey_screenshot_blocks.ts b/x-pack/plugins/synthetics/server/routes/pings/journey_screenshot_blocks.ts new file mode 100644 index 000000000000..6cf0f92b41b4 --- /dev/null +++ b/x-pack/plugins/synthetics/server/routes/pings/journey_screenshot_blocks.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { UMServerLibs } from '../../legacy_uptime/uptime_server'; +import { journeyScreenshotBlocksHandler } from '../../legacy_uptime/routes/pings/journey_screenshot_blocks'; +import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; + +export const createJourneyScreenshotBlocksRoute: SyntheticsRestApiRouteFactory = ( + libs: UMServerLibs +) => ({ + method: 'POST', + path: SYNTHETICS_API_URLS.JOURNEY_SCREENSHOT_BLOCKS, + validate: { + body: schema.object({ + hashes: schema.arrayOf(schema.string()), + }), + }, + handler: async (routeProps) => { + return await journeyScreenshotBlocksHandler(routeProps); + }, +}); diff --git a/x-pack/plugins/synthetics/server/routes/pings/journey_screenshots.ts b/x-pack/plugins/synthetics/server/routes/pings/journey_screenshots.ts new file mode 100644 index 000000000000..a92cf73f1711 --- /dev/null +++ b/x-pack/plugins/synthetics/server/routes/pings/journey_screenshots.ts @@ -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 { schema } from '@kbn/config-schema'; +import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; +import { UMServerLibs } from '../../legacy_uptime/uptime_server'; +import { journeyScreenshotHandler } from '../../legacy_uptime/routes/pings/journey_screenshots'; + +export const createJourneyScreenshotRoute: SyntheticsRestApiRouteFactory = ( + libs: UMServerLibs +) => ({ + method: 'GET', + path: SYNTHETICS_API_URLS.JOURNEY_SCREENSHOT, + validate: { + params: schema.object({ + checkGroup: schema.string(), + stepIndex: schema.number(), + }), + }, + handler: async (routeProps) => { + return await journeyScreenshotHandler(routeProps); + }, +}); diff --git a/x-pack/plugins/synthetics/server/routes/pings/journeys.ts b/x-pack/plugins/synthetics/server/routes/pings/journeys.ts index 9ef10fe6bebb..212f79bd867d 100644 --- a/x-pack/plugins/synthetics/server/routes/pings/journeys.ts +++ b/x-pack/plugins/synthetics/server/routes/pings/journeys.ts @@ -6,9 +6,13 @@ */ import { schema } from '@kbn/config-schema'; -import { API_URLS, SYNTHETICS_API_URLS } from '../../../common/constants'; +import { getJourneyFailedSteps } from '../../legacy_uptime/lib/requests/get_journey_failed_steps'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; import { UMServerLibs } from '../../legacy_uptime/uptime_server'; -import { UMRestApiRouteFactory } from '../../legacy_uptime/routes/types'; +import { + SyntheticsRestApiRouteFactory, + UMRestApiRouteFactory, +} from '../../legacy_uptime/routes/types'; import { getJourneyDetails } from '../../queries/get_journey_details'; export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ @@ -54,9 +58,11 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => }, }); -export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ +export const createJourneyFailedStepsRoute: SyntheticsRestApiRouteFactory = ( + libs: UMServerLibs +) => ({ method: 'GET', - path: API_URLS.JOURNEY_FAILED_STEPS, + path: SYNTHETICS_API_URLS.JOURNEY_FAILED_STEPS, validate: { query: schema.object({ checkGroups: schema.arrayOf(schema.string()), @@ -65,7 +71,7 @@ export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMSer handler: async ({ uptimeEsClient, request, response }): Promise => { const { checkGroups } = request.query; try { - const result = await libs.requests.getJourneyFailedSteps({ + const result = await getJourneyFailedSteps({ uptimeEsClient, checkGroups, }); diff --git a/x-pack/plugins/synthetics/server/routes/pings/last_successful_check.ts b/x-pack/plugins/synthetics/server/routes/pings/last_successful_check.ts new file mode 100644 index 000000000000..7ebc25adec37 --- /dev/null +++ b/x-pack/plugins/synthetics/server/routes/pings/last_successful_check.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { getLastSuccessfulCheckScreenshot } from '../../legacy_uptime/routes/synthetics/last_successful_check'; +import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; +import { UMServerLibs } from '../../legacy_uptime/uptime_server'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; + +export const createLastSuccessfulCheckRoute: SyntheticsRestApiRouteFactory = ( + libs: UMServerLibs +) => ({ + method: 'GET', + path: SYNTHETICS_API_URLS.SYNTHETICS_SUCCESSFUL_CHECK, + validate: { + query: schema.object({ + monitorId: schema.string(), + stepIndex: schema.number(), + timestamp: schema.string(), + location: schema.maybe(schema.string()), + }), + }, + handler: async (routeProps) => { + return await getLastSuccessfulCheckScreenshot(routeProps); + }, +}); diff --git a/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts b/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts index 3e3eb72700ed..3a13b228ba03 100644 --- a/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts +++ b/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts @@ -5,6 +5,8 @@ * 2.0. */ import { KibanaResponse } from '@kbn/core-http-router-server-internal'; +import { checkIndicesReadPrivileges } from './synthetics_service/authentication/check_has_privilege'; +import { SYNTHETICS_INDEX_PATTERN } from '../common/constants'; import { isTestUser, UptimeEsClient } from './legacy_uptime/lib/lib'; import { syntheticsServiceApiKey } from './legacy_uptime/lib/saved_objects/service_api_key'; import { SyntheticsRouteWrapper, SyntheticsStreamingRouteHandler } from './legacy_uptime/routes'; @@ -29,13 +31,11 @@ export const syntheticsRouteWrapper: SyntheticsRouteWrapper = ( // specifically needed for the synthetics service api key generation server.authSavedObjectsClient = savedObjectsClient; - const uptimeEsClient = new UptimeEsClient( - savedObjectsClient, - esClient.asCurrentUser, - false, - coreContext.uiSettings, - request - ); + const uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient.asCurrentUser, { + request, + isDev: false, + uiSettings: coreContext.uiSettings, + }); server.uptimeEsClient = uptimeEsClient; @@ -62,35 +62,48 @@ export const syntheticsRouteWrapper: SyntheticsRouteWrapper = ( // specifically needed for the synthetics service api key generation server.authSavedObjectsClient = savedObjectsClient; - const uptimeEsClient = new UptimeEsClient( - savedObjectsClient, - esClient.asCurrentUser, - Boolean(server.isDev) && !isTestUser(server), + const uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient.asCurrentUser, { + request, uiSettings, - request - ); + isDev: Boolean(server.isDev) && !isTestUser(server), + heartbeatIndices: SYNTHETICS_INDEX_PATTERN, + }); server.uptimeEsClient = uptimeEsClient; - const res = await uptimeRoute.handler({ - uptimeEsClient, - savedObjectsClient, - context, - request, - response, - server, - syntheticsMonitorClient, - }); + try { + const res = await uptimeRoute.handler({ + uptimeEsClient, + savedObjectsClient, + context, + request, + response, + server, + syntheticsMonitorClient, + }); + if (res instanceof KibanaResponse) { + return res; + } - if (res instanceof KibanaResponse) { - return res; + return response.ok({ + body: { + ...res, + ...(await uptimeEsClient.getInspectData(uptimeRoute.path)), + }, + }); + } catch (e) { + if (e.statusCode === 403) { + const privileges = await checkIndicesReadPrivileges(uptimeEsClient); + if (!privileges.has_all_requested) { + return response.forbidden({ + body: { + message: + 'MissingIndicesPrivileges: You do not have permission to read from the synthetics-* indices. Please contact your administrator.', + }, + }); + } + } + throw e; } - - return response.ok({ - body: { - ...res, - ...uptimeEsClient.getInspectData(uptimeRoute.path), - }, - }); }, }); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/authentication/check_has_privilege.ts b/x-pack/plugins/synthetics/server/synthetics_service/authentication/check_has_privilege.ts index 56b7ce8b79c6..7a2dcb544672 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/authentication/check_has_privilege.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/authentication/check_has_privilege.ts @@ -5,9 +5,11 @@ * 2.0. */ +import { SecurityIndexPrivilege } from '@elastic/elasticsearch/lib/api/types'; import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters'; import { getFakeKibanaRequest } from '../utils/fake_kibana_request'; -import { serviceApiKeyPrivileges } from '../get_api_key'; +import { serviceApiKeyPrivileges, syntheticsIndex } from '../get_api_key'; +import { UptimeEsClient } from '../../legacy_uptime/lib/lib'; export const checkHasPrivileges = async ( server: UptimeServerSetup, @@ -22,3 +24,16 @@ export const checkHasPrivileges = async ( }, }); }; + +export const checkIndicesReadPrivileges = async (uptimeEsClient: UptimeEsClient) => { + return await uptimeEsClient.baseESClient.security.hasPrivileges({ + body: { + index: [ + { + names: [syntheticsIndex], + privileges: ['read'] as SecurityIndexPrivilege[], + }, + ], + }, + }); +};