[Uptime] add monitor edit and add pages (#118947) (#119912)

* add monitor edit and add pages

* remove unused imports

* remove unexpected console log

* remove unused values

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Dominique Clarke <doclarke71@gmail.com>
This commit is contained in:
Kibana Machine 2021-11-30 13:43:01 -05:00 committed by GitHub
parent f32d32676d
commit e995ad515f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 620 additions and 40 deletions

View file

@ -6,8 +6,10 @@
*/
import React from 'react';
import { useStartServices } from '../../../hooks';
import type { EnrollmentAPIKey } from '../../../types';
import { PlatformSelector } from './platform_selector';
interface Props {

View file

@ -4,6 +4,16 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { DataStream, PolicyConfig } from '../types';
import { initialValues as defaultHTTPSimpleFields } from './http_context';
import { initialValues as defaultHTTPAdvancedFields } from './http_context_advanced';
import { initialValues as defaultTCPSimpleFields } from './tcp_context';
import { initialValues as defaultICMPSimpleFields } from './icmp_context';
import { initialValues as defaultTCPAdvancedFields } from './tcp_context_advanced';
import { initialValues as defaultBrowserSimpleFields } from './browser_context';
import { initialValues as defaultBrowserAdvancedFields } from './browser_context_advanced';
import { initialValues as defaultTLSFields } from './tls_fields_context';
export {
PolicyConfigContext,
PolicyConfigContextProvider,
@ -61,3 +71,23 @@ export {
export { HTTPContextProvider } from './http_provider';
export { TCPContextProvider } from './tcp_provider';
export { BrowserContextProvider } from './browser_provider';
export { SyntheticsProviders } from './synthetics_context_providers';
export const defaultConfig: PolicyConfig = {
[DataStream.HTTP]: {
...defaultHTTPSimpleFields,
...defaultHTTPAdvancedFields,
...defaultTLSFields,
},
[DataStream.TCP]: {
...defaultTCPSimpleFields,
...defaultTCPAdvancedFields,
...defaultTLSFields,
},
[DataStream.ICMP]: defaultICMPSimpleFields,
[DataStream.BROWSER]: {
...defaultBrowserSimpleFields,
...defaultBrowserAdvancedFields,
...defaultTLSFields,
},
};

View file

@ -10,6 +10,8 @@ import { DataStream } from '../types';
interface IPolicyConfigContext {
setMonitorType: React.Dispatch<React.SetStateAction<DataStream>>;
setName: React.Dispatch<React.SetStateAction<string>>;
setLocations: React.Dispatch<React.SetStateAction<string[]>>;
setIsTLSEnabled: React.Dispatch<React.SetStateAction<boolean>>;
setIsZipUrlTLSEnabled: React.Dispatch<React.SetStateAction<boolean>>;
monitorType: DataStream;
@ -19,6 +21,10 @@ interface IPolicyConfigContext {
defaultIsTLSEnabled?: boolean;
defaultIsZipUrlTLSEnabled?: boolean;
isEditable?: boolean;
defaultName?: string;
name?: string;
defaultLocations?: string[];
locations?: string[];
}
interface IPolicyConfigContextProvider {
@ -26,6 +32,8 @@ interface IPolicyConfigContextProvider {
defaultMonitorType?: DataStream;
defaultIsTLSEnabled?: boolean;
defaultIsZipUrlTLSEnabled?: boolean;
defaultName?: string;
defaultLocations?: string[];
isEditable?: boolean;
}
@ -35,6 +43,12 @@ const defaultContext: IPolicyConfigContext = {
setMonitorType: (_monitorType: React.SetStateAction<DataStream>) => {
throw new Error('setMonitorType was not initialized, set it when you invoke the context');
},
setName: (_name: React.SetStateAction<string>) => {
throw new Error('setName was not initialized, set it when you invoke the context');
},
setLocations: (_locations: React.SetStateAction<string[]>) => {
throw new Error('setLocations was not initialized, set it when you invoke the context');
},
setIsTLSEnabled: (_isTLSEnabled: React.SetStateAction<boolean>) => {
throw new Error('setIsTLSEnabled was not initialized, set it when you invoke the context');
},
@ -47,19 +61,25 @@ const defaultContext: IPolicyConfigContext = {
defaultMonitorType: initialValue, // immutable,
defaultIsTLSEnabled: false,
defaultIsZipUrlTLSEnabled: false,
defaultName: '',
defaultLocations: [],
isEditable: false,
};
export const PolicyConfigContext = createContext(defaultContext);
export const PolicyConfigContextProvider = ({
export function PolicyConfigContextProvider<ExtraFields = unknown>({
children,
defaultMonitorType = initialValue,
defaultIsTLSEnabled = false,
defaultIsZipUrlTLSEnabled = false,
defaultName = '',
defaultLocations = [],
isEditable = false,
}: IPolicyConfigContextProvider) => {
}: IPolicyConfigContextProvider) {
const [monitorType, setMonitorType] = useState<DataStream>(defaultMonitorType);
const [name, setName] = useState<string>(defaultName);
const [locations, setLocations] = useState<string[]>(defaultLocations);
const [isTLSEnabled, setIsTLSEnabled] = useState<boolean>(defaultIsTLSEnabled);
const [isZipUrlTLSEnabled, setIsZipUrlTLSEnabled] = useState<boolean>(defaultIsZipUrlTLSEnabled);
@ -75,6 +95,12 @@ export const PolicyConfigContextProvider = ({
defaultIsTLSEnabled,
defaultIsZipUrlTLSEnabled,
isEditable,
defaultName,
name,
setName,
defaultLocations,
locations,
setLocations,
};
}, [
monitorType,
@ -84,9 +110,15 @@ export const PolicyConfigContextProvider = ({
defaultIsTLSEnabled,
defaultIsZipUrlTLSEnabled,
isEditable,
name,
defaultName,
locations,
defaultLocations,
]);
return <PolicyConfigContext.Provider value={value} children={children} />;
};
}
export const usePolicyConfigContext = () => useContext(PolicyConfigContext);
export function usePolicyConfigContext() {
return useContext(PolicyConfigContext);
}

View file

@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { HTTPFields, TCPFields, ICMPFields, BrowserFields, ITLSFields, DataStream } from '../types';
import {
PolicyConfigContextProvider,
TCPContextProvider,
ICMPSimpleFieldsContextProvider,
HTTPContextProvider,
BrowserContextProvider,
TLSFieldsContextProvider,
} from '.';
interface Props {
children: React.ReactNode;
httpDefaultValues?: HTTPFields;
tcpDefaultValues?: TCPFields;
icmpDefaultValues?: ICMPFields;
browserDefaultValues?: BrowserFields;
tlsDefaultValues?: ITLSFields;
policyDefaultValues?: {
defaultMonitorType: DataStream;
defaultIsTLSEnabled: boolean;
defaultIsZipUrlTLSEnabled: boolean;
defaultName?: string;
defaultLocations?: string[];
isEditable: boolean;
};
}
export const SyntheticsProviders = ({
children,
httpDefaultValues,
tcpDefaultValues,
icmpDefaultValues,
browserDefaultValues,
tlsDefaultValues,
policyDefaultValues,
}: Props) => {
return (
<PolicyConfigContextProvider {...(policyDefaultValues || {})}>
<HTTPContextProvider defaultValues={httpDefaultValues}>
<TCPContextProvider defaultValues={tcpDefaultValues}>
<TLSFieldsContextProvider defaultValues={tlsDefaultValues}>
<ICMPSimpleFieldsContextProvider defaultValues={icmpDefaultValues}>
<BrowserContextProvider defaultValues={browserDefaultValues}>
{children}
</BrowserContextProvider>
</ICMPSimpleFieldsContextProvider>
</TLSFieldsContextProvider>
</TCPContextProvider>
</HTTPContextProvider>
</PolicyConfigContextProvider>
);
};

View file

@ -33,6 +33,7 @@ import { BrowserAdvancedFields } from './browser/advanced_fields';
interface Props {
validate: Validation;
dataStreams?: DataStream[];
children?: React.ReactNode;
}
const dataStreamToString = [
@ -50,7 +51,7 @@ const dataStreamToString = [
},
];
export const CustomFields = memo<Props>(({ validate, dataStreams = [] }) => {
export const CustomFields = memo<Props>(({ validate, dataStreams = [], children }) => {
const { monitorType, setMonitorType, isTLSEnabled, setIsTLSEnabled, isEditable } =
usePolicyConfigContext();
@ -98,6 +99,7 @@ export const CustomFields = memo<Props>(({ validate, dataStreams = [] }) => {
>
<EuiFlexGroup>
<EuiFlexItem>
{children}
{!isEditable && (
<EuiFormRow
label={

View file

@ -54,12 +54,12 @@ export const defaultConfig: PolicyConfig = {
},
};
/**
* Exports Synthetics-specific package policy instructions
* for use in the Ingest app create / edit package policy
*/
export const usePolicy = (name: string) => {
const { isTLSEnabled, isZipUrlTLSEnabled } = usePolicyConfigContext();
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
} = usePolicyConfigContext();
const { fields: httpSimpleFields } = useHTTPSimpleFieldsContext();
const { fields: tcpSimpleFields } = useTCPSimpleFieldsContext();
const { fields: icmpSimpleFields } = useICMPSimpleFieldsContext();
@ -77,6 +77,7 @@ export const usePolicy = (name: string) => {
[isTLSEnabled, isZipUrlTLSEnabled]
);
/* TODO add locations to policy config for synthetics service */
const policyConfig: PolicyConfig = useMemo(
() => ({
[DataStream.HTTP]: {
@ -87,7 +88,7 @@ export const usePolicy = (name: string) => {
...httpSimpleFields[ConfigKeys.METADATA],
...metadata,
},
[ConfigKeys.NAME]: name,
[ConfigKeys.NAME]: fleetPolicyName || monitorName,
} as HTTPFields,
[DataStream.TCP]: {
...tcpSimpleFields,
@ -97,11 +98,11 @@ export const usePolicy = (name: string) => {
...tcpSimpleFields[ConfigKeys.METADATA],
...metadata,
},
[ConfigKeys.NAME]: name,
[ConfigKeys.NAME]: fleetPolicyName || monitorName,
} as TCPFields,
[DataStream.ICMP]: {
...icmpSimpleFields,
[ConfigKeys.NAME]: name,
[ConfigKeys.NAME]: fleetPolicyName || monitorName,
} as ICMPFields,
[DataStream.BROWSER]: {
...browserSimpleFields,
@ -110,7 +111,7 @@ export const usePolicy = (name: string) => {
...browserSimpleFields[ConfigKeys.METADATA],
...metadata,
},
[ConfigKeys.NAME]: name,
[ConfigKeys.NAME]: fleetPolicyName || monitorName,
} as BrowserFields,
}),
[
@ -123,7 +124,8 @@ export const usePolicy = (name: string) => {
browserSimpleFields,
browserAdvancedFields,
tlsFields,
name,
fleetPolicyName,
monitorName,
]
);

View file

@ -8,14 +8,7 @@
import React, { memo } from 'react';
import { PackagePolicyCreateExtensionComponentProps } from '../../../../fleet/public';
import { SyntheticsPolicyCreateExtension } from './synthetics_policy_create_extension';
import {
PolicyConfigContextProvider,
TCPContextProvider,
ICMPSimpleFieldsContextProvider,
HTTPContextProvider,
BrowserContextProvider,
TLSFieldsContextProvider,
} from './contexts';
import { SyntheticsProviders } from './contexts';
/**
* Exports Synthetics-specific package policy instructions
@ -24,19 +17,9 @@ import {
export const SyntheticsPolicyCreateExtensionWrapper =
memo<PackagePolicyCreateExtensionComponentProps>(({ newPolicy, onChange }) => {
return (
<PolicyConfigContextProvider>
<HTTPContextProvider>
<TCPContextProvider>
<TLSFieldsContextProvider>
<ICMPSimpleFieldsContextProvider>
<BrowserContextProvider>
<SyntheticsPolicyCreateExtension newPolicy={newPolicy} onChange={onChange} />
</BrowserContextProvider>
</ICMPSimpleFieldsContextProvider>
</TLSFieldsContextProvider>
</TCPContextProvider>
</HTTPContextProvider>
</PolicyConfigContextProvider>
<SyntheticsProviders>
<SyntheticsPolicyCreateExtension newPolicy={newPolicy} onChange={onChange} />
</SyntheticsProviders>
);
});
SyntheticsPolicyCreateExtensionWrapper.displayName = 'SyntheticsPolicyCreateExtensionWrapper';

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { BrowserFields, ConfigKeys } from '../../fleet_package/types';
import { Formatter, commonFormatters, objectFormatter, arrayFormatter } from './common';
export type BrowserFormatMap = Record<keyof BrowserFields, Formatter>;
export const browserFormatters: BrowserFormatMap = {
[ConfigKeys.METADATA]: (fields) => objectFormatter(fields[ConfigKeys.METADATA]),
[ConfigKeys.SOURCE_ZIP_URL]: null,
[ConfigKeys.SOURCE_ZIP_USERNAME]: null,
[ConfigKeys.SOURCE_ZIP_PASSWORD]: null,
[ConfigKeys.SOURCE_ZIP_FOLDER]: null,
[ConfigKeys.SOURCE_ZIP_PROXY_URL]: null,
[ConfigKeys.SOURCE_INLINE]: null,
[ConfigKeys.PARAMS]: null,
[ConfigKeys.SCREENSHOTS]: null,
[ConfigKeys.SYNTHETICS_ARGS]: (fields) => null,
[ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: null,
[ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: null,
[ConfigKeys.ZIP_URL_TLS_KEY]: null,
[ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: null,
[ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: null,
[ConfigKeys.ZIP_URL_TLS_VERSION]: (fields) =>
arrayFormatter(fields[ConfigKeys.ZIP_URL_TLS_VERSION]),
[ConfigKeys.JOURNEY_FILTERS_MATCH]: null,
[ConfigKeys.JOURNEY_FILTERS_TAGS]: null,
[ConfigKeys.IGNORE_HTTPS_ERRORS]: null,
...commonFormatters,
};

View file

@ -0,0 +1,31 @@
/*
* 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 { ICommonFields, ICustomFields, ConfigKeys } from '../../fleet_package/types';
export type Formatter =
| null
| ((fields: Partial<ICustomFields>) => string | string[] | Record<string, string> | null);
export type CommonFormatMap = Record<keyof ICommonFields | ConfigKeys.NAME, Formatter>;
export const commonFormatters: CommonFormatMap = {
[ConfigKeys.NAME]: null,
[ConfigKeys.MONITOR_TYPE]: null,
[ConfigKeys.SCHEDULE]: (fields) =>
`@every ${fields[ConfigKeys.SCHEDULE]?.number}${fields[ConfigKeys.SCHEDULE]?.unit}`,
[ConfigKeys.APM_SERVICE_NAME]: null,
[ConfigKeys.TAGS]: null,
[ConfigKeys.TIMEOUT]: (fields) => secondsToCronFormatter(fields[ConfigKeys.TIMEOUT]),
};
export const arrayFormatter = (value: string[] = []) => (value.length ? value : null);
export const secondsToCronFormatter = (value: string = '') => (value ? `${value}s` : null);
export const objectFormatter = (value: Record<string, any> = {}) =>
Object.keys(value).length ? value : null;

View file

@ -0,0 +1,37 @@
/*
* 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 { HTTPFields, ConfigKeys } from '../../fleet_package/types';
import { Formatter, commonFormatters, objectFormatter, arrayFormatter } from './common';
import { tlsFormatters } from './tls';
export type HTTPFormatMap = Record<keyof HTTPFields, Formatter>;
export const httpFormatters: HTTPFormatMap = {
[ConfigKeys.METADATA]: (fields) => objectFormatter(fields[ConfigKeys.METADATA]),
[ConfigKeys.URLS]: null,
[ConfigKeys.MAX_REDIRECTS]: null,
[ConfigKeys.USERNAME]: null,
[ConfigKeys.PASSWORD]: null,
[ConfigKeys.PROXY_URL]: null,
[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: (fields) =>
arrayFormatter(fields[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]),
[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: (fields) =>
arrayFormatter(fields[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]),
[ConfigKeys.RESPONSE_BODY_INDEX]: null,
[ConfigKeys.RESPONSE_HEADERS_CHECK]: (fields) =>
objectFormatter(fields[ConfigKeys.RESPONSE_HEADERS_CHECK]),
[ConfigKeys.RESPONSE_HEADERS_INDEX]: null,
[ConfigKeys.RESPONSE_STATUS_CHECK]: (fields) =>
arrayFormatter(fields[ConfigKeys.RESPONSE_STATUS_CHECK]),
[ConfigKeys.REQUEST_BODY_CHECK]: (fields) => fields[ConfigKeys.REQUEST_BODY_CHECK]?.value || null,
[ConfigKeys.REQUEST_HEADERS_CHECK]: (fields) =>
objectFormatter(fields[ConfigKeys.REQUEST_HEADERS_CHECK]),
[ConfigKeys.REQUEST_METHOD_CHECK]: null,
...tlsFormatters,
...commonFormatters,
};

View file

@ -0,0 +1,17 @@
/*
* 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, ConfigKeys } from '../../fleet_package/types';
import { Formatter, commonFormatters, secondsToCronFormatter } from './common';
export type ICMPFormatMap = Record<keyof ICMPFields, Formatter>;
export const icmpFormatters: ICMPFormatMap = {
[ConfigKeys.HOSTS]: null,
[ConfigKeys.WAIT]: (fields) => secondsToCronFormatter(fields[ConfigKeys.WAIT]),
...commonFormatters,
};

View file

@ -0,0 +1,38 @@
/*
* 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 '../../fleet_package/types';
import { httpFormatters, HTTPFormatMap } from './http';
import { tcpFormatters, TCPFormatMap } from './tcp';
import { icmpFormatters, ICMPFormatMap } from './icmp';
import { browserFormatters, BrowserFormatMap } from './browser';
import { commonFormatters, CommonFormatMap } from './common';
type Formatters = HTTPFormatMap & TCPFormatMap & ICMPFormatMap & BrowserFormatMap & CommonFormatMap;
interface FormatterMap {
[DataStream.HTTP]: HTTPFormatMap;
[DataStream.ICMP]: ICMPFormatMap;
[DataStream.TCP]: TCPFormatMap;
[DataStream.BROWSER]: BrowserFormatMap;
}
export const formattersMap: FormatterMap = {
[DataStream.HTTP]: httpFormatters,
[DataStream.ICMP]: icmpFormatters,
[DataStream.TCP]: tcpFormatters,
[DataStream.BROWSER]: browserFormatters,
};
export const formatters: Formatters = {
...httpFormatters,
...icmpFormatters,
...tcpFormatters,
...browserFormatters,
...commonFormatters,
};

View file

@ -0,0 +1,23 @@
/*
* 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, ConfigKeys } from '../../fleet_package/types';
import { Formatter, commonFormatters } from './common';
import { tlsFormatters } from './tls';
export type TCPFormatMap = Record<keyof TCPFields, Formatter>;
export const tcpFormatters: TCPFormatMap = {
[ConfigKeys.METADATA]: null,
[ConfigKeys.HOSTS]: null,
[ConfigKeys.PROXY_URL]: null,
[ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: null,
[ConfigKeys.RESPONSE_RECEIVE_CHECK]: null,
[ConfigKeys.REQUEST_SEND_CHECK]: null,
...tlsFormatters,
...commonFormatters,
};

View file

@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ITLSFields, ConfigKeys } from '../../fleet_package/types';
import { arrayFormatter, Formatter } from './common';
type TLSFormatMap = Record<keyof ITLSFields, Formatter>;
export const tlsFormatters: TLSFormatMap = {
[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: null,
[ConfigKeys.TLS_CERTIFICATE]: null,
[ConfigKeys.TLS_KEY]: null,
[ConfigKeys.TLS_KEY_PASSPHRASE]: null,
[ConfigKeys.TLS_VERIFICATION_MODE]: null,
[ConfigKeys.TLS_VERSION]: (fields) => arrayFormatter(fields[ConfigKeys.TLS_VERSION]),
};

View file

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useEffect, useRef, useState } from 'react';
import { omitBy, isNil } from 'lodash';
import { ConfigKeys, DataStream, Validation, ICustomFields } from '../../fleet_package/types';
import { formatters } from '../formatters';
interface Props {
monitorType: DataStream;
defaultConfig: Partial<ICustomFields>;
config: Partial<ICustomFields>;
validate: Record<DataStream, Validation>;
}
const formatMonitorConfig = (configKeys: ConfigKeys[], config: Partial<ICustomFields>) => {
const formattedMonitor = {} as Record<ConfigKeys, any>;
configKeys.forEach((key) => {
const value = config[key] ?? null;
if (value && formatters[key]) {
formattedMonitor[key] = formatters[key]?.(config);
} else if (value) {
formattedMonitor[key] = value;
}
});
return omitBy(formattedMonitor, isNil) as Partial<ICustomFields>;
};
export const useFormatMonitor = ({ monitorType, defaultConfig, config, validate }: Props) => {
const [formattedMonitor, setFormattedMonitor] = useState<Partial<ICustomFields>>(
formatMonitorConfig(Object.keys(config) as ConfigKeys[], config)
);
const [isValid, setIsValid] = useState(false);
const currentConfig = useRef<Partial<ICustomFields>>(defaultConfig);
useEffect(() => {
const configKeys = Object.keys(config) as ConfigKeys[];
const validationKeys = Object.keys(validate[monitorType]) as ConfigKeys[];
const configDidUpdate = configKeys.some((key) => config[key] !== currentConfig.current[key]);
const isValidT =
!!config.name && !validationKeys.find((key) => validate[monitorType]?.[key]?.(config));
// prevent an infinite loop of updating the policy
if (configDidUpdate) {
const formattedMonitorT = formatMonitorConfig(configKeys, config);
currentConfig.current = config;
setFormattedMonitor(formattedMonitorT);
setIsValid(isValidT);
}
}, [config, currentConfig, validate, monitorType]);
return {
config,
isValid,
formattedMonitor,
};
};

View file

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { defaultConfig, usePolicyConfigContext } from '../fleet_package/contexts';
import { usePolicy } from '../fleet_package/hooks/use_policy';
import { validate } from '../fleet_package/validation';
import { MonitorFields } from './monitor_fields';
import { useFormatMonitor } from './hooks/use_format_monitor';
export const MonitorConfig = () => {
const { monitorType } = usePolicyConfigContext();
/* TODO - Use Effect to make sure the package/index templates are loaded. Wait for it to load before showing view
* then show error message if it fails */
/* raw policy config compatible with the UI. Save this to saved objects */
const policyConfig = usePolicy();
/* Policy config that heartbeat can read. Send this to the service.
This type of helper should ideally be moved to task manager where we are syncing the config.
We can process validation (isValid) and formatting for heartbeat (formattedMonitor) separately
We don't need to save the heartbeat compatible version in saved objects */
useFormatMonitor({
monitorType,
validate,
config: policyConfig[monitorType],
defaultConfig: defaultConfig[monitorType],
});
return <MonitorFields />;
};

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiForm } from '@elastic/eui';
import { DataStream } from '../fleet_package/types';
import { usePolicyConfigContext } from '../fleet_package/contexts';
import { CustomFields } from '../fleet_package/custom_fields';
import { validate } from '../fleet_package/validation';
import { MonitorNameAndLocation } from './monitor_name_location';
export const MonitorFields = () => {
const { monitorType } = usePolicyConfigContext();
return (
<EuiForm id="syntheticsServiceCreateMonitorForm" component="form">
<CustomFields
validate={validate[monitorType]}
dataStreams={[DataStream.HTTP, DataStream.TCP, DataStream.ICMP, DataStream.BROWSER]}
>
<MonitorNameAndLocation />
</CustomFields>
</EuiForm>
);
};

View file

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
import { usePolicyConfigContext } from '../fleet_package/contexts';
export const MonitorNameAndLocation = () => {
const { name, setName } = usePolicyConfigContext();
return (
<>
<EuiFormRow label="Monitor name" fullWidth={true}>
<EuiFieldText
autoFocus={true}
defaultValue={name}
required={true}
fullWidth={true}
name="name"
onChange={(event) => setName(event.target.value)}
/>
</EuiFormRow>
{/* TODO: connect locations */}
{/* <EuiFormRow label={'Service locations'} fullWidth={true}>
<EuiComboBox
selectedOptions={selectedOpts}
fullWidth={true}
options={locOpts}
onChange={(selOptions) => setLocations(selOptions.map(({ value }) => value as string))}
/>
</EuiFormRow> */}
</>
);
};

View file

@ -6,7 +6,17 @@
*/
import React from 'react';
import { useTrackPageview } from '../../../observability/public';
import { SyntheticsProviders } from '../components/fleet_package/contexts';
import { MonitorConfig } from '../components/monitor_management/monitor_config';
export const AddMonitorPage: React.FC = () => {
return null;
useTrackPageview({ app: 'uptime', path: 'add-monitor' });
useTrackPageview({ app: 'uptime', path: 'add-monitor', delay: 15000 });
return (
<SyntheticsProviders>
<MonitorConfig />
</SyntheticsProviders>
);
};

View file

@ -5,8 +5,101 @@
* 2.0.
*/
import React from 'react';
import React, { useMemo } from 'react';
import {
ConfigKeys,
ICustomFields,
ITLSFields,
PolicyConfig,
DataStream,
} from '../components/fleet_package/types';
import { useTrackPageview } from '../../../observability/public';
import { SyntheticsProviders } from '../components/fleet_package/contexts';
import { MonitorConfig } from '../components/monitor_management/monitor_config';
export const EditMonitorPage: React.FC = () => {
return null;
useTrackPageview({ app: 'uptime', path: 'edit-monitor' });
useTrackPageview({ app: 'uptime', path: 'edit-monitor', delay: 15000 });
const {
enableTLS: isTLSEnabled,
enableZipUrlTLS: isZipUrlTLSEnabled,
fullConfig: fullDefaultConfig,
monitorTypeConfig: defaultConfig,
monitorType,
tlsConfig: defaultTLSConfig,
} = useMemo(() => {
/* TODO: fetch current monitor to be edited from saved objects based on url param */
const monitor = {} as Record<ConfigKeys, any>; // fetch
let enableTLS = false;
let enableZipUrlTLS = false;
const getDefaultConfig = () => {
const type: DataStream = monitor[ConfigKeys.MONITOR_TYPE] as DataStream;
const configKeys: ConfigKeys[] = Object.values(ConfigKeys) || ([] as ConfigKeys[]);
const formattedDefaultConfigForMonitorType: ICustomFields = configKeys.reduce<ICustomFields>(
(acc: ICustomFields, key: ConfigKeys) => {
return {
...acc,
key,
};
},
{} as ICustomFields
);
const tlsConfig: ITLSFields = {
[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]:
formattedDefaultConfigForMonitorType[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES],
[ConfigKeys.TLS_CERTIFICATE]:
formattedDefaultConfigForMonitorType[ConfigKeys.TLS_CERTIFICATE],
[ConfigKeys.TLS_KEY]: formattedDefaultConfigForMonitorType[ConfigKeys.TLS_KEY],
[ConfigKeys.TLS_KEY_PASSPHRASE]:
formattedDefaultConfigForMonitorType[ConfigKeys.TLS_KEY_PASSPHRASE],
[ConfigKeys.TLS_VERIFICATION_MODE]:
formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERIFICATION_MODE],
[ConfigKeys.TLS_VERSION]: formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERSION],
};
enableTLS = Boolean(formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERIFICATION_MODE]);
enableZipUrlTLS = Boolean(
formattedDefaultConfigForMonitorType[ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]
);
const formattedDefaultConfig: Partial<PolicyConfig> = {
[type]: formattedDefaultConfigForMonitorType,
};
return {
fullConfig: formattedDefaultConfig,
monitorTypeConfig: formattedDefaultConfigForMonitorType,
tlsConfig,
monitorType: type,
enableTLS,
enableZipUrlTLS,
};
};
return getDefaultConfig();
}, []);
return (
<SyntheticsProviders
policyDefaultValues={{
defaultIsTLSEnabled: isTLSEnabled,
defaultIsZipUrlTLSEnabled: isZipUrlTLSEnabled,
defaultMonitorType: monitorType,
defaultName: defaultConfig?.name || '', // TODO - figure out typing concerns for name
defaultLocations: [], // TODO - figure out locations
isEditable: true,
}}
httpDefaultValues={fullDefaultConfig[DataStream.HTTP]}
tcpDefaultValues={fullDefaultConfig[DataStream.TCP]}
icmpDefaultValues={fullDefaultConfig[DataStream.ICMP]}
browserDefaultValues={fullDefaultConfig[DataStream.BROWSER]}
tlsDefaultValues={defaultTLSConfig}
>
<MonitorConfig />
</SyntheticsProviders>
);
};