mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Monitor management] Enable check for public beta (#128240)
Co-authored-by: Dominique Clarke <dominique.clarke@elastic.co>
This commit is contained in:
parent
0079b3672b
commit
fd1c76691f
41 changed files with 564 additions and 286 deletions
|
@ -21,28 +21,12 @@ const serviceConfig = schema.object({
|
|||
|
||||
const uptimeConfig = schema.object({
|
||||
index: schema.maybe(schema.string()),
|
||||
ui: schema.maybe(
|
||||
schema.object({
|
||||
monitorManagement: schema.maybe(
|
||||
schema.object({
|
||||
enabled: schema.boolean(),
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
service: schema.maybe(serviceConfig),
|
||||
});
|
||||
|
||||
export const config: PluginConfigDescriptor = {
|
||||
exposeToBrowser: {
|
||||
ui: true,
|
||||
},
|
||||
schema: uptimeConfig,
|
||||
};
|
||||
|
||||
export type UptimeConfig = TypeOf<typeof uptimeConfig>;
|
||||
export type ServiceConfig = TypeOf<typeof serviceConfig>;
|
||||
|
||||
export interface UptimeUiConfig {
|
||||
ui?: TypeOf<typeof config.schema>['ui'];
|
||||
}
|
||||
|
|
|
@ -42,4 +42,5 @@ export enum API_URLS {
|
|||
SYNTHETICS_MONITORS = '/internal/uptime/service/monitors',
|
||||
RUN_ONCE_MONITOR = '/internal/uptime/service/monitors/run_once',
|
||||
TRIGGER_MONITOR = '/internal/uptime/service/monitors/trigger',
|
||||
SERVICE_ALLOWED = '/internal/uptime/service/allowed',
|
||||
}
|
||||
|
|
|
@ -15,3 +15,7 @@ export interface MonitorIdParam {
|
|||
export type SyntheticsMonitorSavedObject = SimpleSavedObject<SyntheticsMonitor> & {
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export interface SyntheticsServiceAllowed {
|
||||
serviceAllowed: boolean;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ async function config({ readConfigFile }: FtrConfigProviderContext) {
|
|||
: 'localKibanaIntegrationTestsUser'
|
||||
}`,
|
||||
`--xpack.uptime.service.password=${servicPassword}`,
|
||||
'--xpack.uptime.ui.monitorManagement.enabled=true',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,7 +48,6 @@ import {
|
|||
} from '../components/fleet_package';
|
||||
import { LazySyntheticsCustomAssetsExtension } from '../components/fleet_package/lazy_synthetics_custom_assets_extension';
|
||||
import { Start as InspectorPluginStart } from '../../../../../src/plugins/inspector/public';
|
||||
import { UptimeUiConfig } from '../../common/config';
|
||||
import { CasesUiStart } from '../../../cases/public';
|
||||
|
||||
export interface ClientPluginsSetup {
|
||||
|
@ -87,7 +86,6 @@ export class UptimePlugin
|
|||
constructor(private readonly initContext: PluginInitializerContext) {}
|
||||
|
||||
public setup(core: CoreSetup<ClientPluginsStart, unknown>, plugins: ClientPluginsSetup): void {
|
||||
const config = this.initContext.config.get<UptimeUiConfig>();
|
||||
if (plugins.home) {
|
||||
plugins.home.featureCatalogue.register({
|
||||
id: PLUGIN.ID,
|
||||
|
@ -215,14 +213,7 @@ export class UptimePlugin
|
|||
const [coreStart, corePlugins] = await core.getStartServices();
|
||||
|
||||
const { renderApp } = await import('./render_app');
|
||||
return renderApp(
|
||||
coreStart,
|
||||
plugins,
|
||||
corePlugins,
|
||||
params,
|
||||
config,
|
||||
this.initContext.env.mode.dev
|
||||
);
|
||||
return renderApp(coreStart, plugins, corePlugins, params, this.initContext.env.mode.dev);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
} from '../../common/constants';
|
||||
import { UptimeApp, UptimeAppProps } from './uptime_app';
|
||||
import { ClientPluginsSetup, ClientPluginsStart } from './plugin';
|
||||
import { UptimeUiConfig } from '../../common/config';
|
||||
import { uptimeOverviewNavigatorParams } from './locators/overview';
|
||||
|
||||
export function renderApp(
|
||||
|
@ -25,7 +24,6 @@ export function renderApp(
|
|||
plugins: ClientPluginsSetup,
|
||||
startPlugins: ClientPluginsStart,
|
||||
appMountParameters: AppMountParameters,
|
||||
config: UptimeUiConfig,
|
||||
isDev: boolean
|
||||
) {
|
||||
const {
|
||||
|
@ -77,7 +75,6 @@ export function renderApp(
|
|||
setBadge,
|
||||
appMountParameters,
|
||||
setBreadcrumbs: core.chrome.setBreadcrumbs,
|
||||
config,
|
||||
};
|
||||
|
||||
ReactDOM.render(<UptimeApp {...props} />, appMountParameters.element);
|
||||
|
|
|
@ -35,7 +35,6 @@ import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common
|
|||
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
|
||||
import { UptimeIndexPatternContextProvider } from '../contexts/uptime_index_pattern_context';
|
||||
import { InspectorContextProvider } from '../../../observability/public';
|
||||
import { UptimeUiConfig } from '../../common/config';
|
||||
|
||||
export interface UptimeAppColors {
|
||||
danger: string;
|
||||
|
@ -64,7 +63,6 @@ export interface UptimeAppProps {
|
|||
commonlyUsedRanges: CommonlyUsedRange[];
|
||||
setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void;
|
||||
appMountParameters: AppMountParameters;
|
||||
config: UptimeUiConfig;
|
||||
isDev: boolean;
|
||||
}
|
||||
|
||||
|
@ -80,7 +78,6 @@ const Application = (props: UptimeAppProps) => {
|
|||
setBadge,
|
||||
startPlugins,
|
||||
appMountParameters,
|
||||
config,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -138,11 +135,8 @@ const Application = (props: UptimeAppProps) => {
|
|||
>
|
||||
<InspectorContextProvider>
|
||||
<UptimeAlertsFlyoutWrapper />
|
||||
<PageRouter config={config} />
|
||||
<ActionMenu
|
||||
appMountParameters={appMountParameters}
|
||||
config={config}
|
||||
/>
|
||||
<PageRouter />
|
||||
<ActionMenu appMountParameters={appMountParameters} />
|
||||
</InspectorContextProvider>
|
||||
</RedirectAppLinks>
|
||||
</div>
|
||||
|
|
|
@ -9,19 +9,12 @@ import React from 'react';
|
|||
import { HeaderMenuPortal } from '../../../../../observability/public';
|
||||
import { AppMountParameters } from '../../../../../../../src/core/public';
|
||||
import { ActionMenuContent } from './action_menu_content';
|
||||
import { UptimeConfig } from '../../../../common/config';
|
||||
|
||||
export const ActionMenu = ({
|
||||
appMountParameters,
|
||||
config,
|
||||
}: {
|
||||
appMountParameters: AppMountParameters;
|
||||
config: UptimeConfig;
|
||||
}) => (
|
||||
export const ActionMenu = ({ appMountParameters }: { appMountParameters: AppMountParameters }) => (
|
||||
<HeaderMenuPortal
|
||||
setHeaderActionMenu={appMountParameters.setHeaderActionMenu}
|
||||
theme$={appMountParameters.theme$}
|
||||
>
|
||||
<ActionMenuContent config={config} />
|
||||
<ActionMenuContent />
|
||||
</HeaderMenuPortal>
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ActionMenuContent } from './action_menu_content';
|
|||
|
||||
describe('ActionMenuContent', () => {
|
||||
it('renders alerts dropdown', async () => {
|
||||
const { getByLabelText, getByText } = render(<ActionMenuContent config={{}} />);
|
||||
const { getByLabelText, getByText } = render(<ActionMenuContent />);
|
||||
|
||||
const alertsDropdown = getByLabelText('Open alerts and rules context menu');
|
||||
fireEvent.click(alertsDropdown);
|
||||
|
@ -24,7 +24,7 @@ describe('ActionMenuContent', () => {
|
|||
});
|
||||
|
||||
it('renders settings link', () => {
|
||||
const { getByRole, getByText } = render(<ActionMenuContent config={{}} />);
|
||||
const { getByRole, getByText } = render(<ActionMenuContent />);
|
||||
|
||||
const settingsAnchor = getByRole('link', { name: 'Navigate to the Uptime settings page' });
|
||||
expect(settingsAnchor.getAttribute('href')).toBe('/settings');
|
||||
|
@ -32,7 +32,7 @@ describe('ActionMenuContent', () => {
|
|||
});
|
||||
|
||||
it('renders exploratory view link', () => {
|
||||
const { getByLabelText, getByText } = render(<ActionMenuContent config={{}} />);
|
||||
const { getByLabelText, getByText } = render(<ActionMenuContent />);
|
||||
|
||||
const analyzeAnchor = getByLabelText(
|
||||
'Navigate to the "Explore Data" view to visualize Synthetics/User data'
|
||||
|
@ -43,7 +43,7 @@ describe('ActionMenuContent', () => {
|
|||
});
|
||||
|
||||
it('renders Add Data link', () => {
|
||||
const { getByLabelText, getByText } = render(<ActionMenuContent config={{}} />);
|
||||
const { getByLabelText, getByText } = render(<ActionMenuContent />);
|
||||
|
||||
const addDataAnchor = getByLabelText('Navigate to a tutorial about adding Uptime data');
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import {
|
|||
import { stringifyUrlParams } from '../../../lib/helper/stringify_url_params';
|
||||
import { InspectorHeaderLink } from './inspector_header_link';
|
||||
import { monitorStatusSelector } from '../../../state/selectors';
|
||||
import { UptimeConfig } from '../../../../common/config';
|
||||
|
||||
const ADD_DATA_LABEL = i18n.translate('xpack.uptime.addDataButtonLabel', {
|
||||
defaultMessage: 'Add data',
|
||||
|
@ -39,7 +38,7 @@ const ANALYZE_MESSAGE = i18n.translate('xpack.uptime.analyzeDataButtonLabel.mess
|
|||
'Explore Data allows you to select and filter result data in any dimension and look for the cause or impact of performance problems.',
|
||||
});
|
||||
|
||||
export function ActionMenuContent({ config }: { config: UptimeConfig }): React.ReactElement {
|
||||
export function ActionMenuContent(): React.ReactElement {
|
||||
const kibana = useKibana();
|
||||
const { basePath } = useUptimeSettingsContext();
|
||||
const params = useGetUrlParams();
|
||||
|
@ -77,23 +76,21 @@ export function ActionMenuContent({ config }: { config: UptimeConfig }): React.R
|
|||
|
||||
return (
|
||||
<EuiHeaderLinks gutterSize="xs">
|
||||
{config.ui?.monitorManagement?.enabled && (
|
||||
<EuiHeaderLink
|
||||
aria-label={i18n.translate('xpack.uptime.page_header.manageLink.label', {
|
||||
defaultMessage: 'Navigate to the Uptime monitor management page',
|
||||
})}
|
||||
color="text"
|
||||
data-test-subj="management-page-link"
|
||||
href={history.createHref({
|
||||
pathname: MONITOR_MANAGEMENT_ROUTE + '/all',
|
||||
})}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.page_header.manageLink"
|
||||
defaultMessage="Monitor management"
|
||||
/>
|
||||
</EuiHeaderLink>
|
||||
)}
|
||||
<EuiHeaderLink
|
||||
aria-label={i18n.translate('xpack.uptime.page_header.manageLink.label', {
|
||||
defaultMessage: 'Navigate to the Uptime monitor management page',
|
||||
})}
|
||||
color="text"
|
||||
data-test-subj="management-page-link"
|
||||
href={history.createHref({
|
||||
pathname: MONITOR_MANAGEMENT_ROUTE + '/all',
|
||||
})}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.page_header.manageLink"
|
||||
defaultMessage="Monitor management"
|
||||
/>
|
||||
</EuiHeaderLink>
|
||||
|
||||
<EuiHeaderLink
|
||||
aria-label={i18n.translate('xpack.uptime.page_header.settingsLink.label', {
|
||||
|
|
|
@ -10,15 +10,22 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiButton, EuiFlexItem } from '@elastic/eui';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { MONITOR_ADD_ROUTE } from '../../../common/constants';
|
||||
import { useSyntheticsServiceAllowed } from './hooks/use_service_allowed';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export const AddMonitorBtn = ({ isDisabled }: { isDisabled: boolean }) => {
|
||||
export const AddMonitorBtn = () => {
|
||||
const history = useHistory();
|
||||
|
||||
const { isAllowed, loading } = useSyntheticsServiceAllowed();
|
||||
|
||||
const canSave: boolean = !!useKibana().services?.application?.capabilities.uptime.save;
|
||||
|
||||
return (
|
||||
<EuiFlexItem style={{ alignItems: 'flex-end' }} grow={false} data-test-subj="addMonitorButton">
|
||||
<EuiButton
|
||||
fill
|
||||
isDisabled={isDisabled}
|
||||
isLoading={loading}
|
||||
isDisabled={!canSave || !isAllowed}
|
||||
iconType="plus"
|
||||
data-test-subj="addMonitorBtn"
|
||||
href={history.createHref({
|
||||
|
|
|
@ -71,6 +71,9 @@ describe('useInlineErrors', function () {
|
|||
list: { monitors: [], page: 1, perPage: 10, total: null },
|
||||
loading: { monitorList: false, serviceLocations: false },
|
||||
locations: [],
|
||||
syntheticsService: {
|
||||
loading: false,
|
||||
},
|
||||
},
|
||||
1641081600000,
|
||||
true,
|
||||
|
|
|
@ -70,6 +70,9 @@ describe('useInlineErrorsCount', function () {
|
|||
list: { monitors: [], page: 1, perPage: 10, total: null },
|
||||
loading: { monitorList: false, serviceLocations: false },
|
||||
locations: [],
|
||||
syntheticsService: {
|
||||
loading: false,
|
||||
},
|
||||
},
|
||||
1641081600000,
|
||||
],
|
||||
|
|
|
@ -45,6 +45,9 @@ describe('useExpViewTimeRange', function () {
|
|||
monitorList: false,
|
||||
serviceLocations: loading,
|
||||
},
|
||||
syntheticsService: {
|
||||
loading: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { useDispatch, useSelector } from 'react-redux';
|
||||
import { useEffect } from 'react';
|
||||
import { syntheticsServiceAllowedSelector } from '../../../state/selectors';
|
||||
import { getSyntheticsServiceAllowed } from '../../../state/actions';
|
||||
|
||||
export const useSyntheticsServiceAllowed = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getSyntheticsServiceAllowed.get());
|
||||
}, [dispatch]);
|
||||
|
||||
return useSelector(syntheticsServiceAllowedSelector);
|
||||
};
|
|
@ -6,8 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { MonitorManagementList, MonitorManagementListPageState } from './monitor_list';
|
||||
import { MonitorManagementListResult, Ping } from '../../../../common/runtime_types';
|
||||
import { monitorManagementListSelector } from '../../../state/selectors';
|
||||
|
||||
interface Props {
|
||||
loading: boolean;
|
||||
|
@ -31,6 +33,8 @@ export const InvalidMonitors = ({
|
|||
|
||||
const startIndex = (pageIndex - 1) * pageSize;
|
||||
|
||||
const monitorList = useSelector(monitorManagementListSelector);
|
||||
|
||||
return (
|
||||
<MonitorManagementList
|
||||
pageState={pageState}
|
||||
|
@ -43,7 +47,8 @@ export const InvalidMonitors = ({
|
|||
},
|
||||
error: { monitorList: null, serviceLocations: null },
|
||||
loading: { monitorList: summariesLoading, serviceLocations: false },
|
||||
locations: [],
|
||||
locations: monitorList.locations,
|
||||
syntheticsService: monitorList.syntheticsService,
|
||||
}}
|
||||
onPageStateChange={onPageStateChange}
|
||||
onUpdate={onUpdate}
|
||||
|
|
|
@ -51,6 +51,9 @@ describe('<MonitorManagementList />', () => {
|
|||
monitorList: true,
|
||||
serviceLocations: false,
|
||||
},
|
||||
syntheticsService: {
|
||||
loading: false,
|
||||
},
|
||||
} as MonitorManagementListState,
|
||||
};
|
||||
|
||||
|
|
|
@ -24,7 +24,11 @@ export const TestNowColumn = ({
|
|||
const testNowRun = useSelector(testNowRunSelector(configId));
|
||||
|
||||
if (!configId) {
|
||||
return <>--</>;
|
||||
return (
|
||||
<EuiToolTip content={TEST_NOW_AVAILABLE_LABEL}>
|
||||
<>--</>
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
const testNowClick = () => {
|
||||
|
@ -51,6 +55,13 @@ export const TEST_NOW_ARIA_LABEL = i18n.translate('xpack.uptime.monitorList.test
|
|||
defaultMessage: 'CLick to run test now',
|
||||
});
|
||||
|
||||
export const TEST_NOW_AVAILABLE_LABEL = i18n.translate(
|
||||
'xpack.uptime.monitorList.testNow.available',
|
||||
{
|
||||
defaultMessage: 'Test now is only available for monitors added via Monitor management.',
|
||||
}
|
||||
);
|
||||
|
||||
export const TEST_NOW_LABEL = i18n.translate('xpack.uptime.monitorList.testNow.label', {
|
||||
defaultMessage: 'Test now',
|
||||
});
|
||||
|
|
|
@ -38,7 +38,6 @@ import { MonitorTags } from '../../common/monitor_tags';
|
|||
import { useMonitorHistogram } from './use_monitor_histogram';
|
||||
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
|
||||
import { TestNowColumn } from './columns/test_now_col';
|
||||
import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context';
|
||||
|
||||
interface Props extends MonitorListProps {
|
||||
pageSize: number;
|
||||
|
@ -105,8 +104,6 @@ export const MonitorListComponent: ({
|
|||
}, {});
|
||||
};
|
||||
|
||||
const { config } = useUptimeSettingsContext();
|
||||
|
||||
const columns = [
|
||||
...[
|
||||
{
|
||||
|
@ -209,19 +206,15 @@ export const MonitorListComponent: ({
|
|||
/>
|
||||
),
|
||||
},
|
||||
...(config.ui?.monitorManagement?.enabled
|
||||
? [
|
||||
{
|
||||
align: 'center' as const,
|
||||
field: '',
|
||||
name: TEST_NOW_COLUMN,
|
||||
width: '100px',
|
||||
render: (item: MonitorSummary) => (
|
||||
<TestNowColumn monitorId={item.monitor_id} configId={item.configId} />
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
align: 'center' as const,
|
||||
field: '',
|
||||
name: TEST_NOW_COLUMN,
|
||||
width: '100px',
|
||||
render: (item: MonitorSummary) => (
|
||||
<TestNowColumn monitorId={item.monitor_id} configId={item.configId} />
|
||||
),
|
||||
},
|
||||
...(!hideExtraColumns
|
||||
? [
|
||||
{
|
||||
|
|
|
@ -10,7 +10,6 @@ import { UptimeAppProps } from '../apps/uptime_app';
|
|||
import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../common/constants';
|
||||
import { CommonlyUsedRange } from '../components/common/uptime_date_picker';
|
||||
import { useGetUrlParams } from '../hooks';
|
||||
import { UptimeUiConfig } from '../../common/config';
|
||||
|
||||
export interface UptimeSettingsContextValues {
|
||||
basePath: string;
|
||||
|
@ -19,7 +18,6 @@ export interface UptimeSettingsContextValues {
|
|||
isApmAvailable: boolean;
|
||||
isInfraAvailable: boolean;
|
||||
isLogsAvailable: boolean;
|
||||
config: UptimeUiConfig;
|
||||
commonlyUsedRanges?: CommonlyUsedRange[];
|
||||
isDev?: boolean;
|
||||
}
|
||||
|
@ -39,21 +37,13 @@ const defaultContext: UptimeSettingsContextValues = {
|
|||
isApmAvailable: true,
|
||||
isInfraAvailable: true,
|
||||
isLogsAvailable: true,
|
||||
config: {},
|
||||
isDev: false,
|
||||
};
|
||||
export const UptimeSettingsContext = createContext(defaultContext);
|
||||
|
||||
export const UptimeSettingsContextProvider: React.FC<UptimeAppProps> = ({ children, ...props }) => {
|
||||
const {
|
||||
basePath,
|
||||
isApmAvailable,
|
||||
isInfraAvailable,
|
||||
isLogsAvailable,
|
||||
commonlyUsedRanges,
|
||||
config,
|
||||
isDev,
|
||||
} = props;
|
||||
const { basePath, isApmAvailable, isInfraAvailable, isLogsAvailable, commonlyUsedRanges, isDev } =
|
||||
props;
|
||||
|
||||
const { dateRangeStart, dateRangeEnd } = useGetUrlParams();
|
||||
|
||||
|
@ -65,7 +55,6 @@ export const UptimeSettingsContextProvider: React.FC<UptimeAppProps> = ({ childr
|
|||
isInfraAvailable,
|
||||
isLogsAvailable,
|
||||
commonlyUsedRanges,
|
||||
config,
|
||||
dateRangeStart: dateRangeStart ?? DATE_RANGE_START,
|
||||
dateRangeEnd: dateRangeEnd ?? DATE_RANGE_END,
|
||||
};
|
||||
|
@ -78,7 +67,6 @@ export const UptimeSettingsContextProvider: React.FC<UptimeAppProps> = ({ childr
|
|||
dateRangeStart,
|
||||
dateRangeEnd,
|
||||
commonlyUsedRanges,
|
||||
config,
|
||||
]);
|
||||
|
||||
return <UptimeSettingsContext.Provider value={value} children={children} />;
|
||||
|
|
|
@ -77,6 +77,9 @@ export const mockState: AppState = {
|
|||
monitorList: null,
|
||||
serviceLocations: null,
|
||||
},
|
||||
syntheticsService: {
|
||||
loading: false,
|
||||
},
|
||||
},
|
||||
ml: {
|
||||
mlJob: {
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 { render } from '../../lib/helper/rtl_helpers';
|
||||
|
||||
import * as allowedHook from '../../components/monitor_management/hooks/use_service_allowed';
|
||||
import { ServiceAllowedWrapper } from './service_allowed_wrapper';
|
||||
|
||||
describe('ServiceAllowedWrapper', () => {
|
||||
it('renders expected elements for valid props', async () => {
|
||||
const { findByText } = render(
|
||||
<ServiceAllowedWrapper>
|
||||
<div>Test text</div>
|
||||
</ServiceAllowedWrapper>
|
||||
);
|
||||
|
||||
expect(await findByText('Test text')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders when enabled state is loading', async () => {
|
||||
jest.spyOn(allowedHook, 'useSyntheticsServiceAllowed').mockReturnValue({ loading: true });
|
||||
|
||||
const { findByText } = render(
|
||||
<ServiceAllowedWrapper>
|
||||
<div>Test text</div>
|
||||
</ServiceAllowedWrapper>
|
||||
);
|
||||
|
||||
expect(await findByText('Loading monitor management')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders when enabled state is false', async () => {
|
||||
jest
|
||||
.spyOn(allowedHook, 'useSyntheticsServiceAllowed')
|
||||
.mockReturnValue({ loading: false, isAllowed: false });
|
||||
|
||||
const { findByText } = render(
|
||||
<ServiceAllowedWrapper>
|
||||
<div>Test text</div>
|
||||
</ServiceAllowedWrapper>
|
||||
);
|
||||
|
||||
expect(await findByText('Monitor management')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders when enabled state is true', async () => {
|
||||
jest
|
||||
.spyOn(allowedHook, 'useSyntheticsServiceAllowed')
|
||||
.mockReturnValue({ loading: false, isAllowed: true });
|
||||
|
||||
const { findByText } = render(
|
||||
<ServiceAllowedWrapper>
|
||||
<div>Test text</div>
|
||||
</ServiceAllowedWrapper>
|
||||
);
|
||||
|
||||
expect(await findByText('Test text')).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButton, EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui';
|
||||
import { useSyntheticsServiceAllowed } from '../../components/monitor_management/hooks/use_service_allowed';
|
||||
|
||||
export const ServiceAllowedWrapper: React.FC = ({ children }) => {
|
||||
const { isAllowed, loading } = useSyntheticsServiceAllowed();
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
icon={<EuiLoadingLogo logo="logoKibana" size="xl" />}
|
||||
title={<h2>{LOADING_MONITOR_MANAGEMENT_LABEL}</h2>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// checking for explicit false
|
||||
if (isAllowed === false) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
title={<h2>{MONITOR_MANAGEMENT_LABEL}</h2>}
|
||||
body={<p>{PUBLIC_BETA_DESCRIPTION}</p>}
|
||||
actions={[
|
||||
<EuiButton color="primary" fill isDisabled={true}>
|
||||
{REQUEST_ACCESS_LABEL}
|
||||
</EuiButton>,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
const REQUEST_ACCESS_LABEL = i18n.translate('xpack.uptime.monitorManagement.requestAccess', {
|
||||
defaultMessage: 'Request access',
|
||||
});
|
||||
|
||||
const MONITOR_MANAGEMENT_LABEL = i18n.translate('xpack.uptime.monitorManagement.label', {
|
||||
defaultMessage: 'Monitor management',
|
||||
});
|
||||
|
||||
const LOADING_MONITOR_MANAGEMENT_LABEL = i18n.translate(
|
||||
'xpack.uptime.monitorManagement.loading.label',
|
||||
{
|
||||
defaultMessage: 'Loading monitor management',
|
||||
}
|
||||
);
|
||||
|
||||
const PUBLIC_BETA_DESCRIPTION = i18n.translate(
|
||||
'xpack.uptime.monitorManagement.publicBetaDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Monitor management is available only for selected public beta users. With public\n' +
|
||||
'beta access, you will be able to add HTTP, TCP, ICMP and Browser checks which will\n' +
|
||||
"run on Elastic's managed synthetics service nodes.",
|
||||
}
|
||||
);
|
|
@ -1,47 +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.
|
||||
*/
|
||||
|
||||
// app.test.js
|
||||
import { screen } from '@testing-library/react';
|
||||
import { render } from './lib/helper/rtl_helpers';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import React from 'react';
|
||||
import * as telemetry from './hooks/use_telemetry';
|
||||
import { MONITOR_ADD_ROUTE, MONITOR_EDIT_ROUTE } from '../common/constants';
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { PageRouter } from './routes';
|
||||
|
||||
describe('PageRouter', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(telemetry, 'useUptimeTelemetry').mockImplementation(() => {});
|
||||
});
|
||||
it.each([MONITOR_ADD_ROUTE, MONITOR_EDIT_ROUTE])(
|
||||
'hides ui monitor management pages when feature flag is not enabled',
|
||||
(page) => {
|
||||
const history = createMemoryHistory();
|
||||
history.push(page);
|
||||
render(<PageRouter config={{}} />, { history });
|
||||
|
||||
expect(screen.getByText(/Page not found/i)).toBeInTheDocument();
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
[MONITOR_ADD_ROUTE, 'Add Monitor'],
|
||||
[MONITOR_EDIT_ROUTE, 'Edit Monitor'],
|
||||
])('hides ui monitor management pages when feature flag is not enabled', (page, heading) => {
|
||||
const history = createMemoryHistory();
|
||||
history.push(page);
|
||||
render(<PageRouter config={{ ui: { monitorManagement: { enabled: true } } }} />, {
|
||||
history,
|
||||
});
|
||||
|
||||
expect(screen.getByText(heading)).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -55,14 +55,9 @@ import {
|
|||
import { UptimePageTemplateComponent } from './apps/uptime_page_template';
|
||||
import { apiService } from './state/api/utils';
|
||||
import { useInspectorContext } from '../../observability/public';
|
||||
import { UptimeConfig } from '../common/config';
|
||||
import { AddMonitorBtn } from './components/monitor_management/add_monitor_btn';
|
||||
import { useKibana } from '../../../../src/plugins/kibana_react/public';
|
||||
import { SettingsBottomBar } from './components/settings/settings_bottom_bar';
|
||||
|
||||
interface PageRouterProps {
|
||||
config: UptimeConfig;
|
||||
}
|
||||
import { ServiceAllowedWrapper } from './pages/monitor_management/service_allowed_wrapper';
|
||||
|
||||
type RouteProps = {
|
||||
path: string;
|
||||
|
@ -85,7 +80,7 @@ export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.uptime.overview.h
|
|||
defaultMessage: 'Monitors',
|
||||
});
|
||||
|
||||
const getRoutes = (config: UptimeConfig, canSave: boolean): RouteProps[] => {
|
||||
const getRoutes = (): RouteProps[] => {
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.monitorRoute.title', {
|
||||
|
@ -190,69 +185,77 @@ const getRoutes = (config: UptimeConfig, canSave: boolean): RouteProps[] => {
|
|||
rightSideItems: [],
|
||||
},
|
||||
},
|
||||
...(config.ui?.monitorManagement?.enabled
|
||||
? [
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.addMonitorRoute.title', {
|
||||
defaultMessage: 'Add Monitor | {baseTitle}',
|
||||
values: { baseTitle },
|
||||
}),
|
||||
path: MONITOR_ADD_ROUTE,
|
||||
component: AddMonitorPage,
|
||||
dataTestSubj: 'uptimeMonitorAddPage',
|
||||
telemetryId: UptimePage.MonitorAdd,
|
||||
pageHeader: {
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.addMonitor.pageHeader.title"
|
||||
defaultMessage="Add Monitor"
|
||||
/>
|
||||
),
|
||||
},
|
||||
bottomBar: <MonitorManagementBottomBar />,
|
||||
bottomBarProps: { paddingSize: 'm' as const },
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.editMonitorRoute.title', {
|
||||
defaultMessage: 'Edit Monitor | {baseTitle}',
|
||||
values: { baseTitle },
|
||||
}),
|
||||
path: MONITOR_EDIT_ROUTE,
|
||||
component: EditMonitorPage,
|
||||
dataTestSubj: 'uptimeMonitorEditPage',
|
||||
telemetryId: UptimePage.MonitorEdit,
|
||||
pageHeader: {
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.editMonitor.pageHeader.title"
|
||||
defaultMessage="Edit Monitor"
|
||||
/>
|
||||
),
|
||||
},
|
||||
bottomBar: <MonitorManagementBottomBar />,
|
||||
bottomBarProps: { paddingSize: 'm' as const },
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.monitorManagementRoute.title', {
|
||||
defaultMessage: 'Manage Monitors | {baseTitle}',
|
||||
values: { baseTitle },
|
||||
}),
|
||||
path: MONITOR_MANAGEMENT_ROUTE + '/:type',
|
||||
component: MonitorManagementPage,
|
||||
dataTestSubj: 'uptimeMonitorManagementListPage',
|
||||
telemetryId: UptimePage.MonitorManagement,
|
||||
pageHeader: {
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.monitorManagement.pageHeader.title"
|
||||
defaultMessage="Manage monitors"
|
||||
/>
|
||||
),
|
||||
rightSideItems: [<AddMonitorBtn isDisabled={!canSave} />],
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.addMonitorRoute.title', {
|
||||
defaultMessage: 'Add Monitor | {baseTitle}',
|
||||
values: { baseTitle },
|
||||
}),
|
||||
path: MONITOR_ADD_ROUTE,
|
||||
component: () => (
|
||||
<ServiceAllowedWrapper>
|
||||
<AddMonitorPage />
|
||||
</ServiceAllowedWrapper>
|
||||
),
|
||||
dataTestSubj: 'uptimeMonitorAddPage',
|
||||
telemetryId: UptimePage.MonitorAdd,
|
||||
pageHeader: {
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.addMonitor.pageHeader.title"
|
||||
defaultMessage="Add Monitor"
|
||||
/>
|
||||
),
|
||||
},
|
||||
bottomBar: <MonitorManagementBottomBar />,
|
||||
bottomBarProps: { paddingSize: 'm' as const },
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.editMonitorRoute.title', {
|
||||
defaultMessage: 'Edit Monitor | {baseTitle}',
|
||||
values: { baseTitle },
|
||||
}),
|
||||
path: MONITOR_EDIT_ROUTE,
|
||||
component: () => (
|
||||
<ServiceAllowedWrapper>
|
||||
<EditMonitorPage />
|
||||
</ServiceAllowedWrapper>
|
||||
),
|
||||
dataTestSubj: 'uptimeMonitorEditPage',
|
||||
telemetryId: UptimePage.MonitorEdit,
|
||||
pageHeader: {
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.editMonitor.pageHeader.title"
|
||||
defaultMessage="Edit Monitor"
|
||||
/>
|
||||
),
|
||||
},
|
||||
bottomBar: <MonitorManagementBottomBar />,
|
||||
bottomBarProps: { paddingSize: 'm' as const },
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.uptime.monitorManagementRoute.title', {
|
||||
defaultMessage: 'Manage Monitors | {baseTitle}',
|
||||
values: { baseTitle },
|
||||
}),
|
||||
path: MONITOR_MANAGEMENT_ROUTE + '/:type',
|
||||
component: () => (
|
||||
<ServiceAllowedWrapper>
|
||||
<MonitorManagementPage />
|
||||
</ServiceAllowedWrapper>
|
||||
),
|
||||
dataTestSubj: 'uptimeMonitorManagementListPage',
|
||||
telemetryId: UptimePage.MonitorManagement,
|
||||
pageHeader: {
|
||||
pageTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.monitorManagement.pageHeader.title"
|
||||
defaultMessage="Manage monitors"
|
||||
/>
|
||||
),
|
||||
rightSideItems: [<AddMonitorBtn />],
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
|
@ -268,10 +271,8 @@ const RouteInit: React.FC<Pick<RouteProps, 'path' | 'title' | 'telemetryId'>> =
|
|||
return null;
|
||||
};
|
||||
|
||||
export const PageRouter: FC<PageRouterProps> = ({ config = {} }) => {
|
||||
const canSave: boolean = !!useKibana().services?.application?.capabilities.uptime.save;
|
||||
|
||||
const routes = getRoutes(config, canSave);
|
||||
export const PageRouter: FC = () => {
|
||||
const routes = getRoutes();
|
||||
const { addInspectorRequest } = useInspectorContext();
|
||||
|
||||
apiService.addInspectorRequest = addInspectorRequest;
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
ServiceLocations,
|
||||
FetchMonitorManagementListQueryArgs,
|
||||
} from '../../../common/runtime_types';
|
||||
import { createAsyncAction } from './utils';
|
||||
import { SyntheticsServiceAllowed } from '../../../common/types';
|
||||
|
||||
export const getMonitors = createAction<FetchMonitorManagementListQueryArgs>(
|
||||
'GET_MONITOR_MANAGEMENT_LIST'
|
||||
|
@ -25,3 +27,7 @@ export const getServiceLocationsSuccess = createAction<ServiceLocations>(
|
|||
'GET_SERVICE_LOCATIONS_LIST_SUCCESS'
|
||||
);
|
||||
export const getServiceLocationsFailure = createAction<Error>('GET_SERVICE_LOCATIONS_LIST_FAILURE');
|
||||
|
||||
export const getSyntheticsServiceAllowed = createAsyncAction<void, SyntheticsServiceAllowed>(
|
||||
'GET_SYNTHETICS_SERVICE_ALLOWED'
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
ServiceLocationsApiResponseCodec,
|
||||
ServiceLocationErrors,
|
||||
} from '../../../common/runtime_types';
|
||||
import { SyntheticsMonitorSavedObject } from '../../../common/types';
|
||||
import { SyntheticsMonitorSavedObject, SyntheticsServiceAllowed } from '../../../common/types';
|
||||
import { apiService } from './utils';
|
||||
|
||||
export const setMonitor = async ({
|
||||
|
@ -78,3 +78,7 @@ export interface TestNowResponse {
|
|||
export const testNowMonitor = async (configId: string): Promise<TestNowResponse | undefined> => {
|
||||
return await apiService.get(API_URLS.TRIGGER_MONITOR + `/${configId}`);
|
||||
};
|
||||
|
||||
export const fetchServiceAllowed = async (): Promise<SyntheticsServiceAllowed> => {
|
||||
return await apiService.get(API_URLS.SERVICE_ALLOWED);
|
||||
};
|
||||
|
|
|
@ -12,7 +12,10 @@ import {
|
|||
fetchRunNowMonitorEffect,
|
||||
fetchUpdatedMonitorEffect,
|
||||
} from './monitor_list';
|
||||
import { fetchMonitorManagementEffect } from './monitor_management';
|
||||
import {
|
||||
fetchMonitorManagementEffect,
|
||||
fetchSyntheticsServiceAllowedEffect,
|
||||
} from './monitor_management';
|
||||
import { fetchMonitorStatusEffect } from './monitor_status';
|
||||
import { fetchDynamicSettingsEffect, setDynamicSettingsEffect } from './dynamic_settings';
|
||||
import { fetchPingsEffect, fetchPingHistogramEffect } from './ping';
|
||||
|
@ -48,4 +51,5 @@ export function* rootEffect() {
|
|||
yield fork(generateBlockStatsOnPut);
|
||||
yield fork(pruneBlockCache);
|
||||
yield fork(fetchRunNowMonitorEffect);
|
||||
yield fork(fetchSyntheticsServiceAllowedEffect);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { takeLatest } from 'redux-saga/effects';
|
||||
import { takeLatest, takeLeading } from 'redux-saga/effects';
|
||||
import {
|
||||
getMonitors,
|
||||
getMonitorsSuccess,
|
||||
|
@ -13,8 +13,9 @@ import {
|
|||
getServiceLocations,
|
||||
getServiceLocationsSuccess,
|
||||
getServiceLocationsFailure,
|
||||
getSyntheticsServiceAllowed,
|
||||
} from '../actions';
|
||||
import { fetchMonitorManagementList, fetchServiceLocations } from '../api';
|
||||
import { fetchMonitorManagementList, fetchServiceAllowed, fetchServiceLocations } from '../api';
|
||||
import { fetchEffectFactory } from './fetch_effect';
|
||||
|
||||
export function* fetchMonitorManagementEffect() {
|
||||
|
@ -31,3 +32,14 @@ export function* fetchMonitorManagementEffect() {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function* fetchSyntheticsServiceAllowedEffect() {
|
||||
yield takeLeading(
|
||||
getSyntheticsServiceAllowed.get,
|
||||
fetchEffectFactory(
|
||||
fetchServiceAllowed,
|
||||
getSyntheticsServiceAllowed.success,
|
||||
getSyntheticsServiceAllowed.fail
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,14 +14,17 @@ import {
|
|||
getServiceLocations,
|
||||
getServiceLocationsSuccess,
|
||||
getServiceLocationsFailure,
|
||||
getSyntheticsServiceAllowed,
|
||||
} from '../actions';
|
||||
import { MonitorManagementListResult, ServiceLocations } from '../../../common/runtime_types';
|
||||
import { SyntheticsServiceAllowed } from '../../../common/types';
|
||||
|
||||
export interface MonitorManagementList {
|
||||
error: Record<'monitorList' | 'serviceLocations', Error | null>;
|
||||
loading: Record<'monitorList' | 'serviceLocations', boolean>;
|
||||
list: MonitorManagementListResult;
|
||||
locations: ServiceLocations;
|
||||
syntheticsService: { isAllowed?: boolean; loading: boolean };
|
||||
}
|
||||
|
||||
export const initialState: MonitorManagementList = {
|
||||
|
@ -40,6 +43,9 @@ export const initialState: MonitorManagementList = {
|
|||
monitorList: null,
|
||||
serviceLocations: null,
|
||||
},
|
||||
syntheticsService: {
|
||||
loading: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const monitorManagementListReducer = createReducer(initialState, (builder) => {
|
||||
|
@ -118,5 +124,38 @@ export const monitorManagementListReducer = createReducer(initialState, (builder
|
|||
serviceLocations: action.payload,
|
||||
},
|
||||
})
|
||||
)
|
||||
.addCase(
|
||||
String(getSyntheticsServiceAllowed.get),
|
||||
(state: WritableDraft<MonitorManagementList>) => ({
|
||||
...state,
|
||||
syntheticsService: {
|
||||
isAllowed: state.syntheticsService?.isAllowed,
|
||||
loading: true,
|
||||
},
|
||||
})
|
||||
)
|
||||
.addCase(
|
||||
String(getSyntheticsServiceAllowed.success),
|
||||
(
|
||||
state: WritableDraft<MonitorManagementList>,
|
||||
action: PayloadAction<SyntheticsServiceAllowed>
|
||||
) => ({
|
||||
...state,
|
||||
syntheticsService: {
|
||||
isAllowed: action.payload.serviceAllowed,
|
||||
loading: false,
|
||||
},
|
||||
})
|
||||
)
|
||||
.addCase(
|
||||
String(getSyntheticsServiceAllowed.fail),
|
||||
(state: WritableDraft<MonitorManagementList>, action: PayloadAction<Error>) => ({
|
||||
...state,
|
||||
syntheticsService: {
|
||||
isAllowed: false,
|
||||
loading: false,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -94,3 +94,6 @@ export const networkEventsSelector = ({ networkEvents }: AppState) => networkEve
|
|||
export const syntheticsSelector = ({ synthetics }: AppState) => synthetics;
|
||||
|
||||
export const uptimeWriteSelector = (state: AppState) => state;
|
||||
|
||||
export const syntheticsServiceAllowedSelector = (state: AppState) =>
|
||||
state.monitorManagementList.syntheticsService;
|
||||
|
|
|
@ -60,7 +60,8 @@ export function createUptimeESClient({
|
|||
baseESClient: esClient,
|
||||
async search<DocumentSource extends unknown, TParams extends estypes.SearchRequest>(
|
||||
params: TParams,
|
||||
operationName?: string
|
||||
operationName?: string,
|
||||
index?: string
|
||||
): Promise<{ body: ESSearchResponse<DocumentSource, TParams> }> {
|
||||
let res: any;
|
||||
let esError: any;
|
||||
|
@ -68,7 +69,7 @@ export function createUptimeESClient({
|
|||
savedObjectsClient!
|
||||
);
|
||||
|
||||
const esParams = { index: dynamicSettings!.heartbeatIndices, ...params };
|
||||
const esParams = { index: index ?? dynamicSettings!.heartbeatIndices, ...params };
|
||||
const startTime = process.hrtime();
|
||||
|
||||
const startTimeNow = Date.now();
|
||||
|
@ -84,6 +85,7 @@ export function createUptimeESClient({
|
|||
}
|
||||
|
||||
const inspectableEsQueries = inspectableEsQueriesMap.get(request!);
|
||||
|
||||
if (inspectableEsQueries) {
|
||||
inspectableEsQueries.push(
|
||||
getInspectResponse({
|
||||
|
|
|
@ -85,44 +85,48 @@ export const hydrateSavedObjects = async ({
|
|||
};
|
||||
|
||||
const fetchSampleMonitorDocuments = async (esClient: UptimeESClient, configIds: string[]) => {
|
||||
const data = await esClient.search({
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-15m',
|
||||
lt: 'now',
|
||||
const data = await esClient.search(
|
||||
{
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-15m',
|
||||
lt: 'now',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
terms: {
|
||||
config_id: configIds,
|
||||
{
|
||||
terms: {
|
||||
config_id: configIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
exists: {
|
||||
field: 'summary',
|
||||
{
|
||||
exists: {
|
||||
field: 'summary',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
minimum_should_match: 1,
|
||||
should: [{ exists: { field: 'url.full' } }, { exists: { field: 'url.port' } }],
|
||||
{
|
||||
bool: {
|
||||
minimum_should_match: 1,
|
||||
should: [{ exists: { field: 'url.full' } }, { exists: { field: 'url.port' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
_source: ['url', 'config_id', '@timestamp'],
|
||||
collapse: {
|
||||
field: 'config_id',
|
||||
},
|
||||
},
|
||||
_source: ['url', 'config_id', '@timestamp'],
|
||||
collapse: {
|
||||
field: 'config_id',
|
||||
},
|
||||
},
|
||||
});
|
||||
'getHydrateQuery',
|
||||
'synthetics-*'
|
||||
);
|
||||
|
||||
return data.body.hits.hits.map(
|
||||
({ _source: doc }) => ({ ...(doc as any), timestamp: (doc as any)['@timestamp'] } as Ping)
|
||||
|
|
|
@ -85,6 +85,38 @@ export class ServiceAPIClient {
|
|||
return this.callAPI('POST', { ...data, runOnce: true });
|
||||
}
|
||||
|
||||
async checkIfAccountAllowed() {
|
||||
if (this.authorization) {
|
||||
// in case username/password is provided, we assume it's always allowed
|
||||
return true;
|
||||
}
|
||||
|
||||
const httpsAgent = this.getHttpsAgent();
|
||||
|
||||
if (this.locations.length > 0 && httpsAgent) {
|
||||
// get a url from a random location
|
||||
const url = this.locations[Math.floor(Math.random() * this.locations.length)].url;
|
||||
|
||||
try {
|
||||
const { data } = await axios({
|
||||
method: 'GET',
|
||||
url: url + '/allowed',
|
||||
headers:
|
||||
process.env.NODE_ENV !== 'production' && this.authorization
|
||||
? {
|
||||
Authorization: this.authorization,
|
||||
}
|
||||
: undefined,
|
||||
httpsAgent,
|
||||
});
|
||||
return data.allowed;
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async callAPI(
|
||||
method: 'POST' | 'PUT' | 'DELETE',
|
||||
{ monitors: allMonitors, output, runOnce }: ServiceData
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 { SyntheticsService } from './synthetics_service';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { loggerMock } from './../../../../../../src/core/server/logging/logger.mock';
|
||||
import { UptimeServerSetup } from '../adapters';
|
||||
|
||||
describe('SyntheticsService', () => {
|
||||
const mockEsClient = {
|
||||
search: jest.fn(),
|
||||
};
|
||||
|
||||
const serverMock: UptimeServerSetup = {
|
||||
uptimeEsClient: mockEsClient,
|
||||
authSavedObjectsClient: {
|
||||
bulkUpdate: jest.fn(),
|
||||
},
|
||||
} as unknown as UptimeServerSetup;
|
||||
|
||||
const logger = loggerMock.create();
|
||||
|
||||
it('inits properly', async () => {
|
||||
const service = new SyntheticsService(logger, serverMock, {});
|
||||
service.init();
|
||||
|
||||
expect(service.isAllowed).toEqual(false);
|
||||
expect(service.locations).toEqual([]);
|
||||
});
|
||||
|
||||
it('inits properly with basic auth', async () => {
|
||||
const service = new SyntheticsService(logger, serverMock, {
|
||||
username: 'dev',
|
||||
password: '12345',
|
||||
});
|
||||
|
||||
await service.init();
|
||||
|
||||
expect(service.isAllowed).toEqual(true);
|
||||
});
|
||||
|
||||
it('inits properly with locations with dev', async () => {
|
||||
serverMock.config = { service: { devUrl: 'http://localhost' } };
|
||||
const service = new SyntheticsService(logger, serverMock, {
|
||||
username: 'dev',
|
||||
password: '12345',
|
||||
});
|
||||
|
||||
await service.init();
|
||||
|
||||
expect(service.isAllowed).toEqual(true);
|
||||
expect(service.locations).toEqual([
|
||||
{
|
||||
geo: {
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
},
|
||||
id: 'localhost',
|
||||
label: 'Local Synthetics Service',
|
||||
url: 'http://localhost',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -54,28 +54,25 @@ export class SyntheticsService {
|
|||
private indexTemplateExists?: boolean;
|
||||
private indexTemplateInstalling?: boolean;
|
||||
|
||||
public isAllowed: boolean;
|
||||
|
||||
constructor(logger: Logger, server: UptimeServerSetup, config: ServiceConfig) {
|
||||
this.logger = logger;
|
||||
this.server = server;
|
||||
this.config = config;
|
||||
this.isAllowed = false;
|
||||
|
||||
this.apiClient = new ServiceAPIClient(logger, this.config, this.server.kibanaVersion);
|
||||
|
||||
this.esHosts = getEsHosts({ config: this.config, cloud: server.cloud });
|
||||
|
||||
this.locations = [];
|
||||
|
||||
this.registerServiceLocations();
|
||||
}
|
||||
|
||||
public init() {
|
||||
// TODO: Figure out fake kibana requests to handle API keys on start up
|
||||
// getAPIKeyForSyntheticsService({ server: this.server }).then((apiKey) => {
|
||||
// if (apiKey) {
|
||||
// this.apiKey = apiKey;
|
||||
// }
|
||||
// });
|
||||
this.setupIndexTemplates();
|
||||
public async init() {
|
||||
await this.registerServiceLocations();
|
||||
|
||||
this.isAllowed = await this.apiClient.checkIfAccountAllowed();
|
||||
}
|
||||
|
||||
private setupIndexTemplates() {
|
||||
|
@ -105,12 +102,15 @@ export class SyntheticsService {
|
|||
}
|
||||
}
|
||||
|
||||
public registerServiceLocations() {
|
||||
public async registerServiceLocations() {
|
||||
const service = this;
|
||||
getServiceLocations(service.server).then((result) => {
|
||||
try {
|
||||
const result = await getServiceLocations(service.server);
|
||||
service.locations = result.locations;
|
||||
service.apiClient.locations = result.locations;
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public registerSyncTask(taskManager: TaskManagerSetupContract) {
|
||||
|
@ -130,10 +130,14 @@ export class SyntheticsService {
|
|||
async run() {
|
||||
const { state } = taskInstance;
|
||||
|
||||
service.setupIndexTemplates();
|
||||
service.registerServiceLocations();
|
||||
await service.registerServiceLocations();
|
||||
|
||||
await service.pushConfigs();
|
||||
service.isAllowed = await service.apiClient.checkIfAccountAllowed();
|
||||
|
||||
if (service.isAllowed) {
|
||||
service.setupIndexTemplates();
|
||||
await service.pushConfigs();
|
||||
}
|
||||
|
||||
return { state };
|
||||
},
|
||||
|
|
|
@ -39,14 +39,11 @@ export class Plugin implements PluginType {
|
|||
private server?: UptimeServerSetup;
|
||||
private syntheticService?: SyntheticsService;
|
||||
private readonly telemetryEventsSender: TelemetryEventsSender;
|
||||
private readonly isServiceEnabled?: boolean;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext<UptimeConfig>) {
|
||||
this.initContext = initializerContext;
|
||||
this.logger = initializerContext.logger.get();
|
||||
this.telemetryEventsSender = new TelemetryEventsSender(this.logger);
|
||||
const config = this.initContext.config.get<UptimeConfig>();
|
||||
this.isServiceEnabled = config?.ui?.monitorManagement?.enabled && Boolean(config.service);
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup, plugins: UptimeCorePluginsSetup) {
|
||||
|
@ -84,7 +81,7 @@ export class Plugin implements PluginType {
|
|||
isDev: this.initContext.env.mode.dev,
|
||||
} as UptimeServerSetup;
|
||||
|
||||
if (this.isServiceEnabled && this.server.config.service) {
|
||||
if (this.server.config.service) {
|
||||
this.syntheticService = new SyntheticsService(
|
||||
this.logger,
|
||||
this.server,
|
||||
|
@ -100,7 +97,7 @@ export class Plugin implements PluginType {
|
|||
registerUptimeSavedObjects(
|
||||
core.savedObjects,
|
||||
plugins.encryptedSavedObjects,
|
||||
Boolean(this.isServiceEnabled)
|
||||
Boolean(this.server.config.service)
|
||||
);
|
||||
|
||||
KibanaTelemetryAdapter.registerUsageCollector(
|
||||
|
@ -114,7 +111,7 @@ export class Plugin implements PluginType {
|
|||
}
|
||||
|
||||
public start(coreStart: CoreStart, plugins: UptimeCorePluginsStart) {
|
||||
if (this.isServiceEnabled) {
|
||||
if (this.server?.config.service) {
|
||||
this.savedObjectsClient = new SavedObjectsClient(
|
||||
coreStart.savedObjects.createInternalRepository([syntheticsServiceApiKey.name])
|
||||
);
|
||||
|
@ -131,7 +128,7 @@ export class Plugin implements PluginType {
|
|||
this.server.savedObjectsClient = this.savedObjectsClient;
|
||||
}
|
||||
|
||||
if (this.isServiceEnabled) {
|
||||
if (this.server?.config.service) {
|
||||
this.syntheticService?.init();
|
||||
this.syntheticService?.scheduleSyncTask(plugins.taskManager);
|
||||
if (this.server && this.syntheticService) {
|
||||
|
|
|
@ -38,6 +38,7 @@ import { editSyntheticsMonitorRoute } from './synthetics_service/edit_monitor';
|
|||
import { deleteSyntheticsMonitorRoute } from './synthetics_service/delete_monitor';
|
||||
import { runOnceSyntheticsMonitorRoute } from './synthetics_service/run_once_monitor';
|
||||
import { testNowMonitorRoute } from './synthetics_service/test_now_monitor';
|
||||
import { getServiceAllowedRoute } from './synthetics_service/get_service_allowed';
|
||||
|
||||
export * from './types';
|
||||
export { createRouteWithAuth } from './create_route_with_auth';
|
||||
|
@ -71,4 +72,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [
|
|||
deleteSyntheticsMonitorRoute,
|
||||
runOnceSyntheticsMonitorRoute,
|
||||
testNowMonitorRoute,
|
||||
getServiceAllowedRoute,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { UMRestApiRouteFactory } from '../types';
|
||||
import { API_URLS } from '../../../common/constants';
|
||||
|
||||
export const getServiceAllowedRoute: UMRestApiRouteFactory = () => ({
|
||||
method: 'GET',
|
||||
path: API_URLS.SERVICE_ALLOWED,
|
||||
validate: {},
|
||||
handler: async ({ server }): Promise<any> => {
|
||||
return { serviceAllowed: server.syntheticsService.isAllowed };
|
||||
},
|
||||
});
|
|
@ -23,7 +23,7 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute, server) =>
|
|||
handler: async (context, request, response) => {
|
||||
const { client: esClient } = context.core.elasticsearch;
|
||||
let savedObjectsClient: SavedObjectsClientContract;
|
||||
if (server.config?.ui?.monitorManagement?.enabled) {
|
||||
if (server.config?.service) {
|
||||
savedObjectsClient = context.core.savedObjects.getClient({
|
||||
includedHiddenTypes: [syntheticsServiceApiKey.name],
|
||||
});
|
||||
|
|
|
@ -35,7 +35,6 @@ export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProvi
|
|||
'--xpack.ruleRegistry.write.enabled=true',
|
||||
'--xpack.ruleRegistry.write.enabled=true',
|
||||
'--xpack.ruleRegistry.write.cache.enabled=false',
|
||||
'--xpack.uptime.ui.monitorManagement.enabled=true',
|
||||
'--xpack.uptime.service.password=test',
|
||||
'--xpack.uptime.service.username=localKibanaIntegrationTestsUser',
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['ruleRegistryEnabled'])}`,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue