[Synthetics] Fix private location filter usage (#151968)

Co-authored-by: Dominique Clarke <dominique.clarke@elastic.co>
This commit is contained in:
Shahzad 2023-02-27 10:07:12 +01:00 committed by GitHub
parent 2b9f2ba684
commit aa5d089ef1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 193 additions and 181 deletions

View file

@ -9,7 +9,6 @@ import React from 'react';
import { EuiFilterGroup } from '@elastic/eui'; import { EuiFilterGroup } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { ServiceLocations } from '../../../../../../../common/runtime_types';
import { selectServiceLocationsState } from '../../../../state'; import { selectServiceLocationsState } from '../../../../state';
import { import {
@ -20,10 +19,6 @@ import {
import { useFilters } from './use_filters'; import { useFilters } from './use_filters';
import { FilterButton } from './filter_button'; import { FilterButton } from './filter_button';
export const findLocationItem = (query: string, locations: ServiceLocations) => {
return locations.find(({ id, label }) => query === id || label === query);
};
export const FilterGroup = ({ export const FilterGroup = ({
handleFilterChange, handleFilterChange,
}: { }: {

View file

@ -5,14 +5,14 @@
* 2.0. * 2.0.
*/ */
import React from 'react'; import React, { memo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { FilterGroup } from './filter_group'; import { FilterGroup } from './filter_group';
import { SearchField } from '../search_field'; import { SearchField } from '../search_field';
import { SyntheticsMonitorFilterChangeHandler } from './filter_fields'; import { SyntheticsMonitorFilterChangeHandler } from './filter_fields';
export function ListFilters({ export const ListFilters = memo(function ({
handleFilterChange, handleFilterChange,
}: { }: {
handleFilterChange: SyntheticsMonitorFilterChangeHandler; handleFilterChange: SyntheticsMonitorFilterChangeHandler;
@ -27,4 +27,4 @@ export function ListFilters({
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
); );
} });

View file

@ -40,7 +40,6 @@ describe('useMonitorList', () => {
absoluteTotal: state.monitorList.data.absoluteTotal ?? 0, absoluteTotal: state.monitorList.data.absoluteTotal ?? 0,
pageState: state.monitorList.pageState, pageState: state.monitorList.pageState,
syntheticsMonitors: selectEncryptedSyntheticsSavedMonitors.resultFunc(state.monitorList), syntheticsMonitors: selectEncryptedSyntheticsSavedMonitors.resultFunc(state.monitorList),
overviewStatus: null,
handleFilterChange: jest.fn(), handleFilterChange: jest.fn(),
}; };

View file

@ -8,7 +8,6 @@
import { useCallback, useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useDebounce } from 'react-use'; import { useDebounce } from 'react-use';
import { useOverviewStatus } from './use_overview_status';
import { import {
fetchMonitorListAction, fetchMonitorListAction,
quietFetchMonitorListAction, quietFetchMonitorListAction,
@ -27,8 +26,6 @@ export function useMonitorList() {
const { pageState, loading, loaded, error, data } = useSelector(selectMonitorListState); const { pageState, loading, loaded, error, data } = useSelector(selectMonitorListState);
const syntheticsMonitors = useSelector(selectEncryptedSyntheticsSavedMonitors); const syntheticsMonitors = useSelector(selectEncryptedSyntheticsSavedMonitors);
const { status: overviewStatus } = useOverviewStatus();
const { handleFilterChange } = useMonitorFiltersState(); const { handleFilterChange } = useMonitorFiltersState();
const { lastRefresh } = useSyntheticsRefreshContext(); const { lastRefresh } = useSyntheticsRefreshContext();
@ -50,7 +47,8 @@ export function useMonitorList() {
}, [lastRefresh]); }, [lastRefresh]);
// On initial mount, load the page // On initial mount, load the page
useEffect(() => { useDebounce(
() => {
if (isInitialMount.current) { if (isInitialMount.current) {
if (loaded) { if (loaded) {
dispatch(quietFetchMonitorListAction(pageState)); dispatch(quietFetchMonitorListAction(pageState));
@ -58,9 +56,11 @@ export function useMonitorList() {
dispatch(fetchMonitorListAction.get(pageState)); dispatch(fetchMonitorListAction.get(pageState));
} }
} }
},
100,
// we don't use pageState here, for pageState, useDebounce will handle it // we don't use pageState here, for pageState, useDebounce will handle it
// eslint-disable-next-line react-hooks/exhaustive-deps [dispatch]
}, [dispatch]); );
useDebounce( useDebounce(
() => { () => {
@ -86,7 +86,6 @@ export function useMonitorList() {
loadPage, loadPage,
reloadPage, reloadPage,
absoluteTotal: data.absoluteTotal ?? 0, absoluteTotal: data.absoluteTotal ?? 0,
overviewStatus,
handleFilterChange, handleFilterChange,
}; };
} }

View file

@ -15,7 +15,7 @@ import {
selectOverviewStatus, selectOverviewStatus,
} from '../../../state/overview_status'; } from '../../../state/overview_status';
export function useOverviewStatus() { export function useOverviewStatus({ scopeStatusByLocation }: { scopeStatusByLocation: boolean }) {
const pageState = useSelector(selectOverviewPageState); const pageState = useSelector(selectOverviewPageState);
const { status, error, loaded } = useSelector(selectOverviewStatus); const { status, error, loaded } = useSelector(selectOverviewStatus);
@ -24,16 +24,16 @@ export function useOverviewStatus() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const reload = useCallback(() => { const reload = useCallback(() => {
dispatch(fetchOverviewStatusAction.get(pageState)); dispatch(fetchOverviewStatusAction.get({ pageState, scopeStatusByLocation }));
}, [dispatch, pageState]); }, [dispatch, pageState, scopeStatusByLocation]);
useEffect(() => { useEffect(() => {
if (loaded) { if (loaded) {
dispatch(quietFetchOverviewStatusAction.get(pageState)); dispatch(quietFetchOverviewStatusAction.get({ pageState, scopeStatusByLocation }));
} else { } else {
reload(); reload();
} }
}, [dispatch, reload, lastRefresh, pageState, loaded]); }, [dispatch, reload, lastRefresh, pageState, loaded, scopeStatusByLocation]);
return { return {
status, status,

View file

@ -8,11 +8,13 @@
import React from 'react'; import React from 'react';
import { EuiSpacer } from '@elastic/eui'; import { EuiSpacer } from '@elastic/eui';
import { useSelector } from 'react-redux';
import type { useMonitorList } from '../hooks/use_monitor_list'; import type { useMonitorList } from '../hooks/use_monitor_list';
import { MonitorAsyncError } from './monitor_errors/monitor_async_error'; import { MonitorAsyncError } from './monitor_errors/monitor_async_error';
import { ListFilters } from '../common/monitor_filters/list_filters'; import { ListFilters } from '../common/monitor_filters/list_filters';
import { MonitorList } from './monitor_list_table/monitor_list'; import { MonitorList } from './monitor_list_table/monitor_list';
import { MonitorStats } from './monitor_stats/monitor_stats'; import { MonitorStats } from './monitor_stats/monitor_stats';
import { selectOverviewStatus } from '../../../state/overview_status';
export const MonitorListContainer = ({ export const MonitorListContainer = ({
isEnabled, isEnabled,
@ -30,10 +32,11 @@ export const MonitorListContainer = ({
absoluteTotal, absoluteTotal,
loadPage, loadPage,
reloadPage, reloadPage,
overviewStatus,
handleFilterChange, handleFilterChange,
} = monitorListProps; } = monitorListProps;
const { status: overviewStatus } = useSelector(selectOverviewStatus);
// TODO: Display inline errors in the management table // TODO: Display inline errors in the management table
// const { errorSummaries, loading: errorsLoading } = useInlineErrors({ // const { errorSummaries, loading: errorsLoading } = useInlineErrors({

View file

@ -10,6 +10,7 @@ import { Redirect } from 'react-router-dom';
import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
import { useTrackPageview } from '@kbn/observability-plugin/public'; import { useTrackPageview } from '@kbn/observability-plugin/public';
import { useOverviewStatus } from './hooks/use_overview_status';
import { GETTING_STARTED_ROUTE } from '../../../../../common/constants'; import { GETTING_STARTED_ROUTE } from '../../../../../common/constants';
import { ServiceAllowedWrapper } from '../common/wrappers/service_allowed_wrapper'; import { ServiceAllowedWrapper } from '../common/wrappers/service_allowed_wrapper';
@ -24,7 +25,7 @@ import { useMonitorListBreadcrumbs } from './hooks/use_breadcrumbs';
import { useMonitorList } from './hooks/use_monitor_list'; import { useMonitorList } from './hooks/use_monitor_list';
import * as labels from './management/labels'; import * as labels from './management/labels';
const MonitorPage: React.FC = () => { const MonitorManagementPage: React.FC = () => {
useTrackPageview({ app: 'synthetics', path: 'monitors' }); useTrackPageview({ app: 'synthetics', path: 'monitors' });
useTrackPageview({ app: 'synthetics', path: 'monitors', delay: 15000 }); useTrackPageview({ app: 'synthetics', path: 'monitors', delay: 15000 });
@ -37,6 +38,8 @@ const MonitorPage: React.FC = () => {
enableSynthetics, enableSynthetics,
} = useEnablement(); } = useEnablement();
useOverviewStatus({ scopeStatusByLocation: false });
const monitorListProps = useMonitorList(); const monitorListProps = useMonitorList();
const { syntheticsMonitors, loading: monitorsLoading, absoluteTotal, loaded } = monitorListProps; const { syntheticsMonitors, loading: monitorsLoading, absoluteTotal, loaded } = monitorListProps;
@ -91,6 +94,6 @@ const MonitorPage: React.FC = () => {
export const MonitorsPageWithServiceAllowed = React.memo(() => ( export const MonitorsPageWithServiceAllowed = React.memo(() => (
<ServiceAllowedWrapper> <ServiceAllowedWrapper>
<MonitorPage /> <MonitorManagementPage />
</ServiceAllowedWrapper> </ServiceAllowedWrapper>
)); ));

View file

@ -20,7 +20,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { selectOverviewStatus } from '../../../../state/overview_status'; import { selectOverviewStatus } from '../../../../state/overview_status';
import { AlertsLink } from '../../../common/links/view_alerts'; import { AlertsLink } from '../../../common/links/view_alerts';
import { useRefreshedRange } from '../../../../hooks'; import { useRefreshedRange, useGetUrlParams } from '../../../../hooks';
import { ClientPluginsStart } from '../../../../../../plugin'; import { ClientPluginsStart } from '../../../../../../plugin';
export const OverviewAlerts = () => { export const OverviewAlerts = () => {
@ -33,6 +33,8 @@ export const OverviewAlerts = () => {
const { status } = useSelector(selectOverviewStatus); const { status } = useSelector(selectOverviewStatus);
const { locations } = useGetUrlParams();
const loading = !status?.allIds || status?.allIds.length === 0; const loading = !status?.allIds || status?.allIds.length === 0;
return ( return (
@ -66,6 +68,7 @@ export const OverviewAlerts = () => {
status?.enabledMonitorQueryIds.length > 0 status?.enabledMonitorQueryIds.length > 0
? status?.enabledMonitorQueryIds ? status?.enabledMonitorQueryIds
: ['false-id'], : ['false-id'],
...(locations?.length ? { 'observer.geo.name': locations } : {}),
}, },
filters: [{ field: 'kibana.alert.status', values: ['active', 'recovered'] }], filters: [{ field: 'kibana.alert.status', values: ['active', 'recovered'] }],
color: theme.eui.euiColorVis1, color: theme.eui.euiColorVis1,
@ -92,6 +95,7 @@ export const OverviewAlerts = () => {
status?.enabledMonitorQueryIds.length > 0 status?.enabledMonitorQueryIds.length > 0
? status?.enabledMonitorQueryIds ? status?.enabledMonitorQueryIds
: ['false-id'], : ['false-id'],
...(locations?.length ? { 'observer.geo.name': locations } : {}),
}, },
dataType: 'alerts', dataType: 'alerts',
selectedMetricField: RECORDS_FIELD, selectedMetricField: RECORDS_FIELD,

View file

@ -18,7 +18,7 @@ import { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { selectOverviewStatus } from '../../../../../state/overview_status'; import { selectOverviewStatus } from '../../../../../state/overview_status';
import { OverviewErrorsSparklines } from './overview_errors_sparklines'; import { OverviewErrorsSparklines } from './overview_errors_sparklines';
import { useRefreshedRange } from '../../../../../hooks'; import { useRefreshedRange, useGetUrlParams } from '../../../../../hooks';
import { OverviewErrorsCount } from './overview_errors_count'; import { OverviewErrorsCount } from './overview_errors_count';
export function OverviewErrors() { export function OverviewErrors() {
@ -28,6 +28,8 @@ export function OverviewErrors() {
const { from, to } = useRefreshedRange(6, 'hours'); const { from, to } = useRefreshedRange(6, 'hours');
const params = useGetUrlParams();
return ( return (
<EuiPanel hasShadow={false} hasBorder> <EuiPanel hasShadow={false} hasBorder>
<EuiTitle size="xs"> <EuiTitle size="xs">
@ -43,6 +45,7 @@ export function OverviewErrors() {
from={from} from={from}
to={to} to={to}
monitorIds={status?.enabledMonitorQueryIds ?? []} monitorIds={status?.enabledMonitorQueryIds ?? []}
locations={params.locations}
/> />
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={true}> <EuiFlexItem grow={true}>
@ -50,6 +53,7 @@ export function OverviewErrors() {
from={from} from={from}
to={to} to={to}
monitorIds={status?.enabledMonitorQueryIds ?? []} monitorIds={status?.enabledMonitorQueryIds ?? []}
locations={params.locations}
/> />
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>

View file

@ -16,13 +16,14 @@ interface MonitorErrorsCountProps {
to: string; to: string;
locationLabel?: string; locationLabel?: string;
monitorIds: string[]; monitorIds: string[];
locations?: string[];
} }
export const OverviewErrorsCount = ({ export const OverviewErrorsCount = ({
monitorIds, monitorIds,
from, from,
to, to,
locationLabel, locations,
}: MonitorErrorsCountProps) => { }: MonitorErrorsCountProps) => {
const { observability } = useKibana<ClientPluginsStart>().services; const { observability } = useKibana<ClientPluginsStart>().services;
@ -41,7 +42,7 @@ export const OverviewErrorsCount = ({
time, time,
reportDefinitions: { reportDefinitions: {
'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'], 'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'],
...(locationLabel ? { 'observer.geo.name': [locationLabel] } : {}), ...(locations?.length ? { 'observer.geo.name': locations } : {}),
}, },
dataType: 'synthetics', dataType: 'synthetics',
selectedMetricField: 'monitor_errors', selectedMetricField: 'monitor_errors',

View file

@ -15,8 +15,9 @@ interface Props {
from: string; from: string;
to: string; to: string;
monitorIds: string[]; monitorIds: string[];
locations?: string[];
} }
export const OverviewErrorsSparklines = ({ from, to, monitorIds }: Props) => { export const OverviewErrorsSparklines = ({ from, to, monitorIds, locations }: Props) => {
const { observability } = useKibana<ClientPluginsStart>().services; const { observability } = useKibana<ClientPluginsStart>().services;
const { ExploratoryViewEmbeddable } = observability; const { ExploratoryViewEmbeddable } = observability;
@ -38,6 +39,7 @@ export const OverviewErrorsSparklines = ({ from, to, monitorIds }: Props) => {
seriesType: 'area', seriesType: 'area',
reportDefinitions: { reportDefinitions: {
'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'], 'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'],
...(locations?.length ? { 'observer.geo.name': locations } : {}),
}, },
dataType: 'synthetics', dataType: 'synthetics',
selectedMetricField: 'monitor_errors', selectedMetricField: 'monitor_errors',

View file

@ -21,7 +21,7 @@ function title(t?: number) {
export function OverviewStatus() { export function OverviewStatus() {
const { statusFilter } = useGetUrlParams(); const { statusFilter } = useGetUrlParams();
const { status, error: statusError } = useOverviewStatus(); const { status, error: statusError } = useOverviewStatus({ scopeStatusByLocation: true });
const dispatch = useDispatch(); const dispatch = useDispatch();
const [statusConfig, setStatusConfig] = useState({ const [statusConfig, setStatusConfig] = useState({
up: status?.up, up: status?.up,

View file

@ -100,11 +100,11 @@ export function* upsertMonitorEffect() {
if (action.payload.shouldQuietFetchAfterSuccess !== false) { if (action.payload.shouldQuietFetchAfterSuccess !== false) {
const monitorState = yield select(selectOverviewState); const monitorState = yield select(selectOverviewState);
if (hasPageState(monitorState)) { if (hasPageState(monitorState)) {
yield put(quietFetchOverviewAction.get(monitorState.pageState));
yield put( yield put(
quietFetchOverviewAction.get(monitorState.pageState as MonitorOverviewPageState) quietFetchOverviewStatusAction.get({
); pageState: monitorState.pageState,
yield put( })
quietFetchOverviewStatusAction.get(monitorState.pageState as MonitorOverviewPageState)
); );
} }
} }

View file

@ -12,12 +12,12 @@ import { createAsyncAction } from '../utils/actions';
import { OverviewStatus } from '../../../../../common/runtime_types'; import { OverviewStatus } from '../../../../../common/runtime_types';
export const fetchOverviewStatusAction = createAsyncAction< export const fetchOverviewStatusAction = createAsyncAction<
MonitorOverviewPageState, { pageState: MonitorOverviewPageState; scopeStatusByLocation?: boolean },
OverviewStatus OverviewStatus
>('fetchOverviewStatusAction'); >('fetchOverviewStatusAction');
export const quietFetchOverviewStatusAction = createAsyncAction< export const quietFetchOverviewStatusAction = createAsyncAction<
MonitorOverviewPageState, { pageState: MonitorOverviewPageState; scopeStatusByLocation?: boolean },
OverviewStatus OverviewStatus
>('quietFetchOverviewStatusAction'); >('quietFetchOverviewStatusAction');

View file

@ -11,9 +11,17 @@ import { OverviewStatus, OverviewStatusCodec } from '../../../../../common/runti
import { apiService } from '../../../../utils/api_service'; import { apiService } from '../../../../utils/api_service';
import { toStatusOverviewQueryArgs } from '../overview/api'; import { toStatusOverviewQueryArgs } from '../overview/api';
export const fetchOverviewStatus = async ( export const fetchOverviewStatus = async ({
pageState: MonitorOverviewPageState pageState,
): Promise<OverviewStatus> => { scopeStatusByLocation,
}: {
pageState: MonitorOverviewPageState;
scopeStatusByLocation?: boolean;
}): Promise<OverviewStatus> => {
const params = toStatusOverviewQueryArgs(pageState); const params = toStatusOverviewQueryArgs(pageState);
return apiService.get(SYNTHETICS_API_URLS.OVERVIEW_STATUS, params, OverviewStatusCodec); return apiService.get(
SYNTHETICS_API_URLS.OVERVIEW_STATUS,
{ ...params, scopeStatusByLocation },
OverviewStatusCodec
);
}; };

View file

@ -67,11 +67,11 @@ export class StatusRuleExecutor {
} }
async getAllLocationNames() { async getAllLocationNames() {
const { publicLocations, privateLocations } = await getAllLocations( const { publicLocations, privateLocations } = await getAllLocations({
this.server, server: this.server,
this.syntheticsMonitorClient, syntheticsMonitorClient: this.syntheticsMonitorClient,
this.soClient savedObjectsClient: this.soClient,
); });
publicLocations.forEach((loc) => { publicLocations.forEach((loc) => {
this.locationIdNameMap[loc.label] = loc.id; this.locationIdNameMap[loc.label] = loc.id;

View file

@ -109,15 +109,7 @@ export type UMRouteHandler = ({
subject?: Subject<unknown>; subject?: Subject<unknown>;
}) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>; }) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>;
export type SyntheticsRouteHandler = ({ export interface RouteContext {
uptimeEsClient,
context,
request,
response,
server,
savedObjectsClient,
subject: Subject,
}: {
uptimeEsClient: UptimeEsClient; uptimeEsClient: UptimeEsClient;
context: UptimeRequestHandlerContext; context: UptimeRequestHandlerContext;
request: SyntheticsRequest; request: SyntheticsRequest;
@ -126,7 +118,17 @@ export type SyntheticsRouteHandler = ({
server: UptimeServerSetup; server: UptimeServerSetup;
syntheticsMonitorClient: SyntheticsMonitorClient; syntheticsMonitorClient: SyntheticsMonitorClient;
subject?: Subject<unknown>; subject?: Subject<unknown>;
}) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>; }
export type SyntheticsRouteHandler = ({
uptimeEsClient,
context,
request,
response,
server,
savedObjectsClient,
subject: Subject,
}: RouteContext) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>;
export type SyntheticsStreamingRouteHandler = ({ export type SyntheticsStreamingRouteHandler = ({
uptimeEsClient, uptimeEsClient,

View file

@ -6,11 +6,12 @@
*/ */
import { schema, TypeOf } from '@kbn/config-schema'; import { schema, TypeOf } from '@kbn/config-schema';
import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { SavedObjectsFindResponse } from '@kbn/core/server';
import { SyntheticsService } from '../synthetics_service/synthetics_service'; import { getAllLocations } from '../synthetics_service/get_all_locations';
import { EncryptedSyntheticsMonitor, ServiceLocations } from '../../common/runtime_types'; import { EncryptedSyntheticsMonitor, ServiceLocations } from '../../common/runtime_types';
import { monitorAttributes } from '../../common/types/saved_objects'; import { monitorAttributes } from '../../common/types/saved_objects';
import { syntheticsMonitorType } from '../legacy_uptime/lib/saved_objects/synthetics_monitor'; import { syntheticsMonitorType } from '../legacy_uptime/lib/saved_objects/synthetics_monitor';
import { RouteContext } from '../legacy_uptime/routes';
export const QuerySchema = schema.object({ export const QuerySchema = schema.object({
page: schema.maybe(schema.number()), page: schema.maybe(schema.number()),
@ -40,6 +41,7 @@ export const OverviewStatusSchema = schema.object({
projects: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), projects: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
schedules: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), schedules: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
status: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), status: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
scopeStatusByLocation: schema.maybe(schema.boolean()),
}); });
export type OverviewStatusQuery = TypeOf<typeof OverviewStatusSchema>; export type OverviewStatusQuery = TypeOf<typeof OverviewStatusSchema>;
@ -54,10 +56,8 @@ export const SEARCH_FIELDS = [
'project_id.text', 'project_id.text',
]; ];
export const getMonitors = ( export const getMonitors = async (
request: MonitorsQuery, context: RouteContext
syntheticsService: SyntheticsService,
savedObjectsClient: SavedObjectsClientContract
): Promise<SavedObjectsFindResponse<EncryptedSyntheticsMonitor>> => { ): Promise<SavedObjectsFindResponse<EncryptedSyntheticsMonitor>> => {
const { const {
perPage = 50, perPage = 50,
@ -73,19 +73,19 @@ export const getMonitors = (
searchAfter, searchAfter,
projects, projects,
schedules, schedules,
} = request as MonitorsQuery; } = context.request.query as MonitorsQuery;
const filterStr = getMonitorFilters({ const filterStr = await getMonitorFilters({
filter, filter,
monitorTypes, monitorTypes,
tags, tags,
locations, locations,
serviceLocations: syntheticsService.locations,
projects, projects,
schedules, schedules,
context,
}); });
return savedObjectsClient.find({ return context.savedObjectsClient.find({
type: syntheticsMonitorType, type: syntheticsMonitorType,
perPage, perPage,
page, page,
@ -99,15 +99,14 @@ export const getMonitors = (
}); });
}; };
export const getMonitorFilters = ({ export const getMonitorFilters = async ({
tags, tags,
ports,
filter, filter,
locations, locations,
projects, projects,
monitorTypes, monitorTypes,
schedules, schedules,
serviceLocations, context,
}: { }: {
filter?: string; filter?: string;
tags?: string | string[]; tags?: string | string[];
@ -115,10 +114,9 @@ export const getMonitorFilters = ({
locations?: string | string[]; locations?: string | string[];
projects?: string | string[]; projects?: string | string[];
schedules?: string | string[]; schedules?: string | string[];
ports?: string | string[]; context: RouteContext;
serviceLocations: ServiceLocations;
}) => { }) => {
const locationFilter = parseLocationFilter(serviceLocations, locations); const locationFilter = await parseLocationFilter(context, locations);
return [ return [
filter, filter,
@ -160,18 +158,20 @@ export const getKqlFilter = ({
return `${fieldKey}:"${values}"`; return `${fieldKey}:"${values}"`;
}; };
const parseLocationFilter = (serviceLocations: ServiceLocations, locations?: string | string[]) => { const parseLocationFilter = async (context: RouteContext, locations?: string | string[]) => {
if (!locations) { if (!locations || locations?.length === 0) {
return ''; return '';
} }
const { allLocations } = await getAllLocations(context);
if (Array.isArray(locations)) { if (Array.isArray(locations)) {
return locations return locations
.map((loc) => findLocationItem(loc, serviceLocations)?.id ?? '') .map((loc) => findLocationItem(loc, allLocations)?.id ?? '')
.filter((val) => !!val); .filter((val) => !!val);
} }
return findLocationItem(locations, serviceLocations)?.id ?? ''; return findLocationItem(locations, allLocations)?.id ?? '';
}; };
export const findLocationItem = (query: string, locations: ServiceLocations) => { export const findLocationItem = (query: string, locations: ServiceLocations) => {

View file

@ -45,11 +45,11 @@ export const addSyntheticsProjectMonitorRouteLegacy: SyntheticsStreamingRouteFac
const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request);
const { keep_stale: keepStale, project: projectId } = request.body || {}; const { keep_stale: keepStale, project: projectId } = request.body || {};
const { publicLocations, privateLocations } = await getAllLocations( const { publicLocations, privateLocations } = await getAllLocations({
server, server,
syntheticsMonitorClient, syntheticsMonitorClient,
savedObjectsClient savedObjectsClient,
); });
const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({ const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({

View file

@ -25,13 +25,8 @@ export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory
projectName: schema.string(), projectName: schema.string(),
}), }),
}, },
handler: async ({ handler: async (routeContext): Promise<any> => {
request, const { request, response, savedObjectsClient, server, syntheticsMonitorClient } = routeContext;
response,
savedObjectsClient,
server,
syntheticsMonitorClient,
}): Promise<any> => {
const { projectName } = request.params; const { projectName } = request.params;
const { monitors: monitorsToDelete } = request.body; const { monitors: monitorsToDelete } = request.body;
const decodedProjectName = decodeURI(projectName); const decodedProjectName = decodeURI(projectName);
@ -43,20 +38,20 @@ export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory
}); });
} }
const { saved_objects: monitors } = await getMonitors( const deleteFilter = `${syntheticsMonitorType}.attributes.${
{
filter: `${syntheticsMonitorType}.attributes.${
ConfigKey.PROJECT_ID ConfigKey.PROJECT_ID
}: "${decodedProjectName}" AND ${getKqlFilter({ }: "${decodedProjectName}" AND ${getKqlFilter({
field: 'journey_id', field: 'journey_id',
values: monitorsToDelete.map((id: string) => `${id}`), values: monitorsToDelete.map((id: string) => `${id}`),
})}`, })}`;
fields: [],
perPage: 500, const { saved_objects: monitors } = await getMonitors({
...routeContext,
request: {
...request,
query: { ...request.query, filter: deleteFilter, fields: [], perPage: 500 },
}, },
syntheticsMonitorClient.syntheticsService, });
savedObjectsClient
);
const { const {
integrations: { writeIntegrationPolicies }, integrations: { writeIntegrationPolicies },

View file

@ -65,7 +65,8 @@ export const getAllSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () =>
validate: { validate: {
query: QuerySchema, query: QuerySchema,
}, },
handler: async ({ request, savedObjectsClient, syntheticsMonitorClient }): Promise<any> => { handler: async (routeContext): Promise<any> => {
const { request, savedObjectsClient, syntheticsMonitorClient } = routeContext;
const totalCountQuery = async () => { const totalCountQuery = async () => {
if (isMonitorsQueryFiltered(request.query)) { if (isMonitorsQueryFiltered(request.query)) {
return savedObjectsClient.find({ return savedObjectsClient.find({
@ -77,7 +78,7 @@ export const getAllSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () =>
}; };
const [queryResult, totalCount] = await Promise.all([ const [queryResult, totalCount] = await Promise.all([
getMonitors(request.query, syntheticsMonitorClient.syntheticsService, savedObjectsClient), getMonitors(routeContext),
totalCountQuery(), totalCountQuery(),
]); ]);
@ -101,7 +102,9 @@ export const getSyntheticsMonitorOverviewRoute: SyntheticsRestApiRouteFactory =
validate: { validate: {
query: QuerySchema, query: QuerySchema,
}, },
handler: async ({ request, savedObjectsClient, syntheticsMonitorClient }): Promise<any> => { handler: async (routeContext): Promise<any> => {
const { request, savedObjectsClient } = routeContext;
const { const {
sortField, sortField,
sortOrder, sortOrder,
@ -109,9 +112,9 @@ export const getSyntheticsMonitorOverviewRoute: SyntheticsRestApiRouteFactory =
locations: queriedLocations, locations: queriedLocations,
} = request.query as MonitorsQuery; } = request.query as MonitorsQuery;
const filtersStr = getMonitorFilters({ const filtersStr = await getMonitorFilters({
...request.query, ...request.query,
serviceLocations: syntheticsMonitorClient.syntheticsService.locations, context: routeContext,
}); });
const allMonitorConfigs = await getAllMonitors({ const allMonitorConfigs = await getAllMonitors({

View file

@ -25,21 +25,24 @@ export const getSyntheticsProjectMonitorsRoute: SyntheticsRestApiRouteFactory =
}), }),
query: querySchema, query: querySchema,
}, },
handler: async ({ handler: async (routeContext): Promise<any> => {
const {
request, request,
response,
server: { logger }, server: { logger },
savedObjectsClient, } = routeContext;
syntheticsMonitorClient,
}): Promise<any> => {
const { projectName } = request.params; const { projectName } = request.params;
const { per_page: perPage = 500, search_after: searchAfter } = request.query; const { per_page: perPage = 500, search_after: searchAfter } = request.query;
const decodedProjectName = decodeURI(projectName); const decodedProjectName = decodeURI(projectName);
const decodedSearchAfter = searchAfter ? decodeURI(searchAfter) : undefined; const decodedSearchAfter = searchAfter ? decodeURI(searchAfter) : undefined;
try { try {
const { saved_objects: monitors, total } = await getMonitors( const { saved_objects: monitors, total } = await getMonitors({
{ ...routeContext,
request: {
...request,
query: {
...request.query,
filter: `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}: "${decodedProjectName}"`, filter: `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}: "${decodedProjectName}"`,
fields: [ConfigKey.JOURNEY_ID, ConfigKey.CONFIG_HASH], fields: [ConfigKey.JOURNEY_ID, ConfigKey.CONFIG_HASH],
perPage, perPage,
@ -47,9 +50,8 @@ export const getSyntheticsProjectMonitorsRoute: SyntheticsRestApiRouteFactory =
sortOrder: 'asc', sortOrder: 'asc',
searchAfter: decodedSearchAfter ? [...decodedSearchAfter.split(',')] : undefined, searchAfter: decodedSearchAfter ? [...decodedSearchAfter.split(',')] : undefined,
}, },
syntheticsMonitorClient.syntheticsService, },
savedObjectsClient });
);
const projectMonitors = monitors.map((monitor) => ({ const projectMonitors = monitors.map((monitor) => ({
journey_id: monitor.attributes[ConfigKey.JOURNEY_ID], journey_id: monitor.attributes[ConfigKey.JOURNEY_ID],
hash: monitor.attributes[ConfigKey.CONFIG_HASH] || '', hash: monitor.attributes[ConfigKey.CONFIG_HASH] || '',

View file

@ -6,20 +6,16 @@
*/ */
import { intersection } from 'lodash'; import { intersection } from 'lodash';
import datemath, { Unit } from '@kbn/datemath'; import datemath, { Unit } from '@kbn/datemath';
import { SavedObjectsClientContract } from '@kbn/core/server';
import moment from 'moment'; import moment from 'moment';
import { ConfigKey } from '../../../common/runtime_types'; import { ConfigKey } from '../../../common/runtime_types';
import { import {
getAllMonitors, getAllMonitors,
processMonitors, processMonitors,
} from '../../saved_objects/synthetics_monitor/get_all_monitors'; } from '../../saved_objects/synthetics_monitor/get_all_monitors';
import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
import { queryMonitorStatus } from '../../queries/query_monitor_status'; import { queryMonitorStatus } from '../../queries/query_monitor_status';
import { SYNTHETICS_API_URLS } from '../../../common/constants'; import { SYNTHETICS_API_URLS } from '../../../common/constants';
import { UMServerLibs } from '../../legacy_uptime/uptime_server'; import { UMServerLibs } from '../../legacy_uptime/uptime_server';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; import { RouteContext, SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes';
import { UptimeEsClient } from '../../legacy_uptime/lib/lib';
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
import { getMonitorFilters, OverviewStatusSchema, OverviewStatusQuery } from '../common'; import { getMonitorFilters, OverviewStatusSchema, OverviewStatusQuery } from '../common';
/** /**
@ -40,14 +36,10 @@ export function periodToMs(schedule: { number: string; unit: Unit }) {
* Subsequently, fetch the status for each monitor per location in the data streams. * Subsequently, fetch the status for each monitor per location in the data streams.
* @returns The counts of up/down/disabled monitor by location, and a map of each monitor:location status. * @returns The counts of up/down/disabled monitor by location, and a map of each monitor:location status.
*/ */
export async function getStatus( export async function getStatus(context: RouteContext, params: OverviewStatusQuery) {
server: UptimeServerSetup, const { uptimeEsClient, syntheticsMonitorClient, savedObjectsClient, server } = context;
uptimeEsClient: UptimeEsClient,
soClient: SavedObjectsClientContract, const { query, locations: queryLocations, scopeStatusByLocation = true } = params;
syntheticsMonitorClient: SyntheticsMonitorClient,
params: OverviewStatusQuery
) {
const { query, locations: queryLocations } = params;
/** /**
* Walk through all monitor saved objects, bucket IDs by disabled/enabled status. * Walk through all monitor saved objects, bucket IDs by disabled/enabled status.
* *
@ -55,12 +47,13 @@ export async function getStatus(
* latest ping for all enabled monitors. * latest ping for all enabled monitors.
*/ */
const filtersStr = getMonitorFilters({ const filtersStr = await getMonitorFilters({
...params, ...params,
serviceLocations: syntheticsMonitorClient.syntheticsService.locations, context,
}); });
const allMonitors = await getAllMonitors({ const allMonitors = await getAllMonitors({
soClient, soClient: savedObjectsClient,
search: query ? `${query}*` : undefined, search: query ? `${query}*` : undefined,
filter: filtersStr, filter: filtersStr,
fields: [ fields: [
@ -83,12 +76,13 @@ export async function getStatus(
disabledMonitorsCount, disabledMonitorsCount,
projectMonitorsCount, projectMonitorsCount,
monitorQueryIdToConfigIdMap, monitorQueryIdToConfigIdMap,
} = await processMonitors(allMonitors, server, soClient, syntheticsMonitorClient); } = await processMonitors(allMonitors, server, savedObjectsClient, syntheticsMonitorClient);
// Account for locations filter // Account for locations filter
const queryLocationsArray = const queryLocationsArray =
queryLocations && !Array.isArray(queryLocations) ? [queryLocations] : queryLocations; queryLocations && !Array.isArray(queryLocations) ? [queryLocations] : queryLocations;
const listOfLocationAfterFilter = queryLocationsArray const listOfLocationAfterFilter =
queryLocationsArray && scopeStatusByLocation
? intersection(listOfLocations, queryLocationsArray) ? intersection(listOfLocations, queryLocationsArray)
: listOfLocations; : listOfLocations;
@ -128,20 +122,10 @@ export const createGetCurrentStatusRoute: SyntheticsRestApiRouteFactory = (libs:
validate: { validate: {
query: OverviewStatusSchema, query: OverviewStatusSchema,
}, },
handler: async ({ handler: async (routeContext): Promise<any> => {
server, const { request } = routeContext;
uptimeEsClient,
savedObjectsClient,
syntheticsMonitorClient,
request,
}): Promise<any> => {
const params = request.query as OverviewStatusQuery; const params = request.query as OverviewStatusQuery;
return await getStatus( return await getStatus(routeContext, params);
server,
uptimeEsClient,
savedObjectsClient,
syntheticsMonitorClient,
params
);
}, },
}); });

View file

@ -14,14 +14,14 @@ export const getServiceLocationsRoute: SyntheticsRestApiRouteFactory = () => ({
path: API_URLS.SERVICE_LOCATIONS, path: API_URLS.SERVICE_LOCATIONS,
validate: {}, validate: {},
handler: async ({ server, savedObjectsClient, syntheticsMonitorClient }): Promise<any> => { handler: async ({ server, savedObjectsClient, syntheticsMonitorClient }): Promise<any> => {
const { publicLocations, privateLocations, throttling } = await getAllLocations( const { throttling, allLocations } = await getAllLocations({
server, server,
syntheticsMonitorClient, syntheticsMonitorClient,
savedObjectsClient savedObjectsClient,
); });
return { return {
locations: [...publicLocations, ...privateLocations], locations: allLocations,
throttling, throttling,
}; };
}, },

View file

@ -87,11 +87,11 @@ export const processMonitors = async (
const getLocationLabel = async (locationId: string) => { const getLocationLabel = async (locationId: string) => {
if (!allLocations) { if (!allLocations) {
const { publicLocations, privateLocations } = await getAllLocations( const { publicLocations, privateLocations } = await getAllLocations({
server, server,
syntheticsMonitorClient, syntheticsMonitorClient,
soClient savedObjectsClient: soClient,
); });
allLocations = [...publicLocations, ...privateLocations]; allLocations = [...publicLocations, ...privateLocations];
} }

View file

@ -10,20 +10,29 @@ import { getServiceLocations } from './get_service_locations';
import { SyntheticsMonitorClient } from './synthetics_monitor/synthetics_monitor_client'; import { SyntheticsMonitorClient } from './synthetics_monitor/synthetics_monitor_client';
import { UptimeServerSetup } from '../legacy_uptime/lib/adapters/framework'; import { UptimeServerSetup } from '../legacy_uptime/lib/adapters/framework';
export async function getAllLocations( export async function getAllLocations({
server: UptimeServerSetup, syntheticsMonitorClient,
syntheticsMonitorClient: SyntheticsMonitorClient, savedObjectsClient,
savedObjectsClient: SavedObjectsClientContract server,
) { }: {
server: UptimeServerSetup;
syntheticsMonitorClient: SyntheticsMonitorClient;
savedObjectsClient: SavedObjectsClientContract;
}) {
try { try {
const [privateLocations, { locations: publicLocations, throttling }] = await Promise.all([ const [privateLocations, { locations: publicLocations, throttling }] = await Promise.all([
getPrivateLocations(syntheticsMonitorClient, savedObjectsClient), getPrivateLocations(syntheticsMonitorClient, savedObjectsClient),
getServicePublicLocations(server, syntheticsMonitorClient), getServicePublicLocations(server, syntheticsMonitorClient),
]); ]);
return { publicLocations, privateLocations, throttling }; return {
publicLocations,
privateLocations,
throttling,
allLocations: [...publicLocations, ...privateLocations],
};
} catch (e) { } catch (e) {
server.logger.error(e); server.logger.error(e);
return { publicLocations: [], privateLocations: [] }; return { publicLocations: [], privateLocations: [], allLocations: [] };
} }
} }

View file

@ -120,11 +120,11 @@ export class ProjectMonitorFormatter {
} }
init = async () => { init = async () => {
const locationsPromise = getAllLocations( const locationsPromise = getAllLocations({
this.server, server: this.server,
this.syntheticsMonitorClient, syntheticsMonitorClient: this.syntheticsMonitorClient,
this.savedObjectsClient savedObjectsClient: this.savedObjectsClient,
); });
const existingMonitorsPromise = this.getProjectMonitorsForProject(); const existingMonitorsPromise = this.getProjectMonitorsForProject();
const [locations, existingMonitors] = await Promise.all([ const [locations, existingMonitors] = await Promise.all([

View file

@ -30,7 +30,6 @@ import { syntheticsMonitorType } from '../../legacy_uptime/lib/saved_objects/syn
export class SyntheticsMonitorClient { export class SyntheticsMonitorClient {
public syntheticsService: SyntheticsService; public syntheticsService: SyntheticsService;
public privateLocationAPI: SyntheticsPrivateLocation; public privateLocationAPI: SyntheticsPrivateLocation;
constructor(syntheticsService: SyntheticsService, server: UptimeServerSetup) { constructor(syntheticsService: SyntheticsService, server: UptimeServerSetup) {