diff --git a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts
index 7e61e0a9e5e08..a0875d9df4977 100644
--- a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts
+++ b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts
@@ -12,6 +12,7 @@ export enum SYNTHETICS_API_URLS {
OVERVIEW_STATUS = `/internal/synthetics/overview_status`,
INDEX_SIZE = `/internal/synthetics/index_size`,
PARAMS = `/synthetics/params`,
+ PRIVATE_LOCATIONS = `/synthetics/private_locations`,
SYNC_GLOBAL_PARAMS = `/synthetics/sync_global_params`,
ENABLE_DEFAULT_ALERTING = `/synthetics/enable_default_alerting`,
JOURNEY = `/internal/synthetics/journey/{checkGroup}`,
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.test.tsx
index e343e9faf1aec..ce80c7d97e71e 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.test.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.test.tsx
@@ -5,12 +5,25 @@
* 2.0.
*/
-import { renderHook } from '@testing-library/react-hooks';
-import { defaultCore, WrappedHelper } from '../../../../utils/testing';
-
+import { renderHook, act } from '@testing-library/react-hooks';
+import { WrappedHelper } from '../../../../utils/testing';
+import { getServiceLocations } from '../../../../state/service_locations';
+import { setAddingNewPrivateLocation } from '../../../../state/private_locations';
import { useLocationsAPI } from './use_locations_api';
+import * as locationAPI from '../../../../state/private_locations/api';
+import * as reduxHooks from 'react-redux';
describe('useLocationsAPI', () => {
+ const dispatch = jest.fn();
+ const addAPI = jest.spyOn(locationAPI, 'addSyntheticsPrivateLocations').mockResolvedValue({
+ locations: [],
+ });
+ const deletedAPI = jest.spyOn(locationAPI, 'deleteSyntheticsPrivateLocations').mockResolvedValue({
+ locations: [],
+ });
+ const getAPI = jest.spyOn(locationAPI, 'getSyntheticsPrivateLocations');
+ jest.spyOn(reduxHooks, 'useDispatch').mockReturnValue(dispatch);
+
it('returns expected results', () => {
const { result } = renderHook(() => useLocationsAPI(), {
wrapper: WrappedHelper,
@@ -22,20 +35,15 @@ describe('useLocationsAPI', () => {
privateLocations: [],
})
);
- expect(defaultCore.savedObjects.client.get).toHaveBeenCalledWith(
- 'synthetics-privates-locations',
- 'synthetics-privates-locations-singleton'
- );
+ expect(getAPI).toHaveBeenCalledTimes(1);
});
- defaultCore.savedObjects.client.get = jest.fn().mockReturnValue({
- attributes: {
- locations: [
- {
- id: 'Test',
- agentPolicyId: 'testPolicy',
- },
- ],
- },
+ jest.spyOn(locationAPI, 'getSyntheticsPrivateLocations').mockResolvedValue({
+ locations: [
+ {
+ id: 'Test',
+ agentPolicyId: 'testPolicy',
+ } as any,
+ ],
});
it('returns expected results after data', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLocationsAPI(), {
@@ -71,77 +79,50 @@ describe('useLocationsAPI', () => {
await waitForNextUpdate();
- result.current.onSubmit({
- id: 'new',
- agentPolicyId: 'newPolicy',
- label: 'new',
+ act(() => {
+ result.current.onSubmit({
+ id: 'new',
+ agentPolicyId: 'newPolicy',
+ label: 'new',
+ concurrentMonitors: 1,
+ geo: {
+ lat: 0,
+ lon: 0,
+ },
+ });
+ });
+
+ await waitForNextUpdate();
+
+ expect(addAPI).toHaveBeenCalledWith({
concurrentMonitors: 1,
+ id: 'newPolicy',
geo: {
lat: 0,
lon: 0,
},
+ label: 'new',
+ agentPolicyId: 'newPolicy',
});
-
- await waitForNextUpdate();
-
- expect(defaultCore.savedObjects.client.create).toHaveBeenCalledWith(
- 'synthetics-privates-locations',
- {
- locations: [
- { id: 'Test', agentPolicyId: 'testPolicy' },
- {
- concurrentMonitors: 1,
- id: 'newPolicy',
- geo: {
- lat: 0,
- lon: 0,
- },
- label: 'new',
- agentPolicyId: 'newPolicy',
- },
- ],
- },
- { id: 'synthetics-privates-locations-singleton', overwrite: true }
- );
+ expect(dispatch).toBeCalledWith(setAddingNewPrivateLocation(false));
+ expect(dispatch).toBeCalledWith(getServiceLocations());
});
it('deletes location on delete', async () => {
- defaultCore.savedObjects.client.get = jest.fn().mockReturnValue({
- attributes: {
- locations: [
- {
- id: 'Test',
- agentPolicyId: 'testPolicy',
- },
- {
- id: 'Test1',
- agentPolicyId: 'testPolicy1',
- },
- ],
- },
- });
-
const { result, waitForNextUpdate } = renderHook(() => useLocationsAPI(), {
wrapper: WrappedHelper,
});
await waitForNextUpdate();
- result.current.onDelete('Test');
+ act(() => {
+ result.current.onDelete('Test');
+ });
await waitForNextUpdate();
- expect(defaultCore.savedObjects.client.create).toHaveBeenLastCalledWith(
- 'synthetics-privates-locations',
- {
- locations: [
- {
- id: 'Test1',
- agentPolicyId: 'testPolicy1',
- },
- ],
- },
- { id: 'synthetics-privates-locations-singleton', overwrite: true }
- );
+ expect(deletedAPI).toHaveBeenLastCalledWith('Test');
+ expect(dispatch).toBeCalledWith(setAddingNewPrivateLocation(false));
+ expect(dispatch).toBeCalledWith(getServiceLocations());
});
});
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts
index 6b35e79152c87..8678328445a62 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts
@@ -7,12 +7,13 @@
import { useFetcher } from '@kbn/observability-plugin/public';
import { useState } from 'react';
-import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useDispatch } from 'react-redux';
+import { getServiceLocations } from '../../../../state/service_locations';
import { setAddingNewPrivateLocation } from '../../../../state/private_locations';
import {
+ addSyntheticsPrivateLocations,
+ deleteSyntheticsPrivateLocations,
getSyntheticsPrivateLocations,
- setSyntheticsPrivateLocations,
} from '../../../../state/private_locations/api';
import { PrivateLocation } from '../../../../../../../common/runtime_types';
@@ -21,31 +22,29 @@ export const useLocationsAPI = () => {
const [deleteId, setDeleteId] = useState();
const [privateLocations, setPrivateLocations] = useState([]);
- const { savedObjects } = useKibana().services;
-
const dispatch = useDispatch();
const setIsAddingNew = (val: boolean) => dispatch(setAddingNewPrivateLocation(val));
const { loading: fetchLoading } = useFetcher(async () => {
- const result = await getSyntheticsPrivateLocations(savedObjects?.client!);
- setPrivateLocations(result);
+ const result = await getSyntheticsPrivateLocations();
+ setPrivateLocations(result.locations);
return result;
}, []);
const { loading: saveLoading } = useFetcher(async () => {
- if (privateLocations && formData) {
- const existingLocations = privateLocations.filter((loc) => loc.id !== formData.agentPolicyId);
-
- const result = await setSyntheticsPrivateLocations(savedObjects?.client!, {
- locations: [...(existingLocations ?? []), { ...formData, id: formData.agentPolicyId }],
+ if (formData) {
+ const result = await addSyntheticsPrivateLocations({
+ ...formData,
+ id: formData.agentPolicyId,
});
setPrivateLocations(result.locations);
setFormData(undefined);
setIsAddingNew(false);
+ dispatch(getServiceLocations());
return result;
}
- }, [formData, privateLocations]);
+ }, [formData]);
const onSubmit = (data: PrivateLocation) => {
setFormData(data);
@@ -57,14 +56,13 @@ export const useLocationsAPI = () => {
const { loading: deleteLoading } = useFetcher(async () => {
if (deleteId) {
- const result = await setSyntheticsPrivateLocations(savedObjects?.client!, {
- locations: (privateLocations ?? []).filter((loc) => loc.id !== deleteId),
- });
+ const result = await deleteSyntheticsPrivateLocations(deleteId);
setPrivateLocations(result.locations);
setDeleteId(undefined);
+ dispatch(getServiceLocations());
return result;
}
- }, [deleteId, privateLocations]);
+ }, [deleteId]);
return {
formData,
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx
index 38bdac2592fb7..a001c503697b1 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx
@@ -33,8 +33,10 @@ export const LocationForm = ({
hasPermissions: boolean;
}) => {
const { data } = useSelector(selectAgentPolicies);
- const { control, register } = useFormContext();
+ const { control, register, watch } = useFormContext();
const { errors } = useFormState();
+ const selectedPolicyId = watch('agentPolicyId');
+ const selectedPolicy = data?.items.find((item) => item.id === selectedPolicyId);
const tagsList = privateLocations.reduce((acc, item) => {
const tags = item.tags || [];
@@ -97,6 +99,39 @@ export const LocationForm = ({
}
+
+
+ {selectedPolicy?.agents === 0 && (
+
+
+ {
+
+
+
+ ),
+ }}
+ />
+ }
+
+
+ )}
>
);
@@ -109,6 +144,13 @@ export const AGENT_CALLOUT_TITLE = i18n.translate(
}
);
+export const AGENT_MISSING_CALLOUT_TITLE = i18n.translate(
+ 'xpack.synthetics.monitorManagement.agentMissingCallout.title',
+ {
+ defaultMessage: 'Selected agent policy has no agents',
+ }
+);
+
export const LOCATION_NAME_LABEL = i18n.translate(
'xpack.synthetics.monitorManagement.locationName',
{
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.tsx
index dd140ffd3b3f5..e8246aa13221e 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/manage_private_locations.tsx
@@ -26,7 +26,6 @@ export const ManagePrivateLocations = () => {
const dispatch = useDispatch();
const isAddingNew = useSelector(selectAddingNewPrivateLocation);
-
const setIsAddingNew = (val: boolean) => dispatch(setAddingNewPrivateLocation(val));
const { onSubmit, loading, privateLocations, onDelete, deleteLoading } = useLocationsAPI();
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx
index a1a4f586e63bf..2c44bc80fed36 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/policy_name.tsx
@@ -6,7 +6,7 @@
*/
import React from 'react';
-import { EuiLink, EuiLoadingSpinner, EuiText, EuiTextColor } from '@elastic/eui';
+import { EuiBadge, EuiLink, EuiLoadingSpinner, EuiText, EuiTextColor } from '@elastic/eui';
import { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { useSyntheticsSettingsContext } from '../../../contexts';
@@ -28,7 +28,7 @@ export const PolicyName = ({ agentPolicyId }: { agentPolicyId: string }) => {
return (
- {canReadAgentPolicies && (
+ {canReadAgentPolicies ? (
{policy ? (
@@ -40,7 +40,14 @@ export const PolicyName = ({ agentPolicyId }: { agentPolicyId: string }) => {
)}
+ ) : (
+ agentPolicyId
)}
+
+
+ {AGENTS_LABEL}
+ {policy?.agents}
+
);
};
@@ -48,3 +55,7 @@ export const PolicyName = ({ agentPolicyId }: { agentPolicyId: string }) => {
const POLICY_IS_DELETED = i18n.translate('xpack.synthetics.monitorManagement.deletedPolicy', {
defaultMessage: 'Policy is deleted',
});
+
+const AGENTS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.agents', {
+ defaultMessage: 'Agents: ',
+});
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/route_config.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/route_config.ts
index ee9b6c34bd8f9..51ddf14ee5f67 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/route_config.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/route_config.ts
@@ -17,32 +17,27 @@ export const getSettingsRouteConfig = (
syntheticsPath: string,
baseTitle: string
) => {
+ const sharedProps = {
+ title: i18n.translate('xpack.synthetics.settingsRoute.title', {
+ defaultMessage: 'Settings | {baseTitle}',
+ values: { baseTitle },
+ }),
+ component: SettingsPage,
+ pageHeader: getSettingsPageHeader(history, syntheticsPath),
+ dataTestSubj: 'syntheticsSettingsPage',
+ pageSectionProps: {
+ paddingSize: 'm',
+ },
+ };
+
return [
{
- title: i18n.translate('xpack.synthetics.settingsRoute.title', {
- defaultMessage: 'Settings | {baseTitle}',
- values: { baseTitle },
- }),
+ ...sharedProps,
path: SETTINGS_ROUTE,
- component: SettingsPage,
- dataTestSubj: 'syntheticsSettingsPage',
- pageSectionProps: {
- paddingSize: 'm',
- },
- pageHeader: getSettingsPageHeader(history, syntheticsPath),
},
{
- title: i18n.translate('xpack.synthetics.settingsRoute.title', {
- defaultMessage: 'Settings | {baseTitle}',
- values: { baseTitle },
- }),
+ ...sharedProps,
path: SYNTHETICS_SETTINGS_ROUTE,
- component: SettingsPage,
- dataTestSubj: 'syntheticsSettingsPage',
- pageSectionProps: {
- paddingSize: 'm',
- },
- pageHeader: getSettingsPageHeader(history, syntheticsPath),
},
] as RouteProps[];
};
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts
index 3c48696c685b9..50683fdd87a35 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts
@@ -5,14 +5,10 @@
* 2.0.
*/
-import { SavedObjectsClientContract } from '@kbn/core/public';
-import { SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
+import { SYNTHETICS_API_URLS } from '../../../../../common/constants';
+import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../../common/runtime_types';
import { apiService } from '../../../../utils/api_service/api_service';
import { AgentPoliciesList } from '.';
-import {
- privateLocationsSavedObjectId,
- privateLocationsSavedObjectName,
-} from '../../../../../common/saved_objects/private_locations';
const FLEET_URLS = {
AGENT_POLICIES: '/api/fleet/agent_policies',
@@ -33,26 +29,18 @@ export const fetchAgentPolicies = async (): Promise => {
);
};
-export const setSyntheticsPrivateLocations = async (
- client: SavedObjectsClientContract,
- privateLocations: SyntheticsPrivateLocations
-) => {
- const result = await client.create(privateLocationsSavedObjectName, privateLocations, {
- id: privateLocationsSavedObjectId,
- overwrite: true,
- });
+export const addSyntheticsPrivateLocations = async (
+ newLocation: PrivateLocation
+): Promise => {
+ return await apiService.post(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS, newLocation);
+};
- return result.attributes;
+export const getSyntheticsPrivateLocations = async (): Promise => {
+ return await apiService.get(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS);
};
-export const getSyntheticsPrivateLocations = async (client: SavedObjectsClientContract) => {
- try {
- const obj = await client.get(
- privateLocationsSavedObjectName,
- privateLocationsSavedObjectId
- );
- return obj?.attributes.locations ?? [];
- } catch (getErr) {
- return [];
- }
+export const deleteSyntheticsPrivateLocations = async (
+ locationId: string
+): Promise => {
+ return await apiService.delete(SYNTHETICS_API_URLS.PRIVATE_LOCATIONS + `/${locationId}`);
};
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts
index ced9c212e83cf..371e054476c79 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts
@@ -45,7 +45,6 @@ export const rootEffect = function* root(): Generator {
fork(fetchAgentPoliciesEffect),
fork(fetchDynamicSettingsEffect),
fork(setDynamicSettingsEffect),
- fork(fetchAgentPoliciesEffect),
fork(fetchAlertConnectorsEffect),
fork(syncGlobalParamsEffect),
fork(enableDefaultAlertingEffect),
diff --git a/x-pack/plugins/synthetics/server/routes/index.ts b/x-pack/plugins/synthetics/server/routes/index.ts
index 6978a38b2be46..b394c53f20142 100644
--- a/x-pack/plugins/synthetics/server/routes/index.ts
+++ b/x-pack/plugins/synthetics/server/routes/index.ts
@@ -44,6 +44,9 @@ import { getHasIntegrationMonitorsRoute } from './fleet/get_has_integration_moni
import { addSyntheticsParamsRoute } from './settings/add_param';
import { enableDefaultAlertingRoute } from './default_alerts/enable_default_alert';
import { getDefaultAlertingRoute } from './default_alerts/get_default_alert';
+import { addPrivateLocationRoute } from './settings/private_locations/add_private_location';
+import { deletePrivateLocationRoute } from './settings/private_locations/delete_private_location';
+import { getPrivateLocationsRoute } from './settings/private_locations/get_private_locations';
export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [
addSyntheticsMonitorRoute,
@@ -77,6 +80,9 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [
getDefaultAlertingRoute,
updateDefaultAlertingRoute,
createJourneyRoute,
+ addPrivateLocationRoute,
+ deletePrivateLocationRoute,
+ getPrivateLocationsRoute,
];
export const syntheticsAppStreamingApiRoutes: SyntheticsStreamingRouteFactory[] = [
diff --git a/x-pack/plugins/synthetics/server/routes/settings/private_locations/add_private_location.ts b/x-pack/plugins/synthetics/server/routes/settings/private_locations/add_private_location.ts
new file mode 100644
index 0000000000000..9edd4bf53f81a
--- /dev/null
+++ b/x-pack/plugins/synthetics/server/routes/settings/private_locations/add_private_location.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { getAllPrivateLocations } from './get_private_locations';
+import {
+ privateLocationsSavedObjectId,
+ privateLocationsSavedObjectName,
+} from '../../../../common/saved_objects/private_locations';
+import { SyntheticsRestApiRouteFactory } from '../../../legacy_uptime/routes';
+import { SYNTHETICS_API_URLS } from '../../../../common/constants';
+import { PrivateLocation } from '../../../../common/runtime_types';
+
+export const PrivateLocationSchema = schema.object({
+ label: schema.string(),
+ id: schema.string(),
+ agentPolicyId: schema.string(),
+ concurrentMonitors: schema.number(),
+ tags: schema.maybe(schema.arrayOf(schema.string())),
+ geo: schema.maybe(
+ schema.object({
+ lat: schema.oneOf([schema.number(), schema.string()]),
+ lon: schema.oneOf([schema.number(), schema.string()]),
+ })
+ ),
+});
+
+export const addPrivateLocationRoute: SyntheticsRestApiRouteFactory = () => ({
+ method: 'POST',
+ path: SYNTHETICS_API_URLS.PRIVATE_LOCATIONS,
+ validate: {
+ body: PrivateLocationSchema,
+ },
+ writeAccess: true,
+ handler: async ({ request, server, savedObjectsClient }): Promise => {
+ const location = request.body as PrivateLocation;
+
+ const { locations } = await getAllPrivateLocations(savedObjectsClient);
+ const existingLocations = locations.filter((loc) => loc.id !== location.agentPolicyId);
+
+ const result = await savedObjectsClient.create(
+ privateLocationsSavedObjectName,
+ { locations: [...existingLocations, location] },
+ {
+ id: privateLocationsSavedObjectId,
+ overwrite: true,
+ }
+ );
+
+ return result.attributes;
+ },
+});
diff --git a/x-pack/plugins/synthetics/server/routes/settings/private_locations/delete_private_location.ts b/x-pack/plugins/synthetics/server/routes/settings/private_locations/delete_private_location.ts
new file mode 100644
index 0000000000000..7360adfbc0b60
--- /dev/null
+++ b/x-pack/plugins/synthetics/server/routes/settings/private_locations/delete_private_location.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { getAllPrivateLocations } from './get_private_locations';
+import { SyntheticsRestApiRouteFactory } from '../../../legacy_uptime/routes';
+import { SYNTHETICS_API_URLS } from '../../../../common/constants';
+import {
+ privateLocationsSavedObjectId,
+ privateLocationsSavedObjectName,
+} from '../../../../common/saved_objects/private_locations';
+
+export const deletePrivateLocationRoute: SyntheticsRestApiRouteFactory = () => ({
+ method: 'DELETE',
+ path: SYNTHETICS_API_URLS.PRIVATE_LOCATIONS + '/{locationId}',
+ validate: {
+ params: schema.object({
+ locationId: schema.string({ minLength: 1, maxLength: 1024 }),
+ }),
+ },
+ writeAccess: true,
+ handler: async ({ savedObjectsClient, request, server }): Promise => {
+ const { locationId } = request.params as { locationId: string };
+
+ const { locations } = await getAllPrivateLocations(savedObjectsClient);
+ const remainingLocations = locations.filter((loc) => loc.id !== locationId);
+
+ const result = await savedObjectsClient.create(
+ privateLocationsSavedObjectName,
+ { locations: remainingLocations },
+ {
+ id: privateLocationsSavedObjectId,
+ overwrite: true,
+ }
+ );
+
+ return result.attributes;
+ },
+});
diff --git a/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts b/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts
new file mode 100644
index 0000000000000..82b1f4f4e0e8a
--- /dev/null
+++ b/x-pack/plugins/synthetics/server/routes/settings/private_locations/get_private_locations.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
+import { SyntheticsPrivateLocations } from '../../../../common/runtime_types';
+import { SyntheticsRestApiRouteFactory } from '../../../legacy_uptime/routes';
+import { SYNTHETICS_API_URLS } from '../../../../common/constants';
+import {
+ privateLocationsSavedObjectId,
+ privateLocationsSavedObjectName,
+} from '../../../../common/saved_objects/private_locations';
+
+export const getPrivateLocationsRoute: SyntheticsRestApiRouteFactory = () => ({
+ method: 'GET',
+ path: SYNTHETICS_API_URLS.PRIVATE_LOCATIONS,
+ validate: {},
+ handler: async ({ savedObjectsClient }): Promise => {
+ return await getAllPrivateLocations(savedObjectsClient);
+ },
+});
+
+export const getAllPrivateLocations = async (
+ savedObjectsClient: SavedObjectsClientContract
+): Promise => {
+ try {
+ const obj = await savedObjectsClient.get(
+ privateLocationsSavedObjectName,
+ privateLocationsSavedObjectId
+ );
+ return obj?.attributes ?? { locations: [] };
+ } catch (getErr) {
+ return { locations: [] };
+ }
+};