From 2b2c730a4ac21a65f4a402b11509edba0bc8a2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Wed, 18 Mar 2026 13:33:34 +0000 Subject: [PATCH 1/9] add table with links to policies --- .../common/components/monitor_inspect.tsx | 133 ++++++++++++- .../monitor_add_edit/steps/index.tsx | 6 +- .../steps/inspect_monitor_portal.tsx | 4 +- .../state/monitor_management/api.ts | 16 +- .../monitor_cruds/inspect_monitor.test.ts | 180 ++++++++++++++++++ .../routes/monitor_cruds/inspect_monitor.ts | 94 ++++++++- 6 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index 911f5376fee82..7fb2ee2cfeddc 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -10,6 +10,8 @@ import { useFetcher } from '@kbn/observability-shared-plugin/public'; import { i18n } from '@kbn/i18n'; import { + EuiBasicTable, + EuiCallOut, EuiFlyout, EuiButton, EuiCodeBlock, @@ -17,6 +19,7 @@ import { EuiTitle, EuiFlyoutFooter, EuiHorizontalRule, + EuiLink, EuiSpacer, EuiFlyoutBody, EuiToolTip, @@ -24,21 +27,26 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; +import type { EuiBasicTableColumn } from '@elastic/eui'; import yaml from 'js-yaml'; import { useSyntheticsSettingsContext } from '../../../contexts'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; import type { SyntheticsMonitor } from '../../../../../../common/runtime_types'; import { MonitorTypeEnum } from '../../../../../../common/runtime_types'; -import type { MonitorInspectResponse } from '../../../state/monitor_management/api'; +import type { + MonitorInspectResponse, + PackagePolicyLink, +} from '../../../state/monitor_management/api'; import { inspectMonitorAPI } from '../../../state/monitor_management/api'; interface InspectorProps { isValid: boolean; monitorFields: SyntheticsMonitor; + isEditFlow?: boolean; } -export const MonitorInspect = ({ isValid, monitorFields }: InspectorProps) => { +export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: InspectorProps) => { const { isDev } = useSyntheticsSettingsContext(); const [hideParams, setHideParams] = useState(() => !isDev); @@ -111,6 +119,15 @@ export const MonitorInspect = ({ isValid, monitorFields }: InspectorProps) => { {formatContent(data.result, asJson)} {data.decodedCode && } + {isEditFlow && data.packagePolicyLinks.length > 0 && ( + <> + + + + )} ) : loading && !error ? ( @@ -149,6 +166,81 @@ export const MonitorInspect = ({ isValid, monitorFields }: InspectorProps) => { ); }; +const PackagePolicyLinksTable = ({ + links, + hasMissingReferences, +}: { + links: PackagePolicyLink[]; + hasMissingReferences: boolean; +}) => { + const { basePath } = useSyntheticsSettingsContext(); + + const columns: Array> = [ + { + field: 'locationLabel', + name: PRIVATE_LOCATION_LABEL, + }, + { + field: 'agentPolicyId', + name: AGENT_POLICY_LABEL, + render: (agentPolicyId: string) => ( + + {agentPolicyId} + + ), + }, + { + field: 'packagePolicyId', + name: PACKAGE_POLICY_LABEL, + render: (packagePolicyId: string, item: PackagePolicyLink) => ( + + {packagePolicyId} + + ), + }, + ]; + + return ( + <> + +

{LINKED_POLICIES_LABEL}

+
+ + {hasMissingReferences && ( + <> + +

{MISSING_REFERENCES_DESCRIPTION}

+
+ + + )} + + + ); +}; + // @ts-ignore: Unused variable // tslint:disable-next-line: no-unused-variable const MonitorCode = ({ code }: { code: string }) => ( @@ -219,3 +311,40 @@ const HIDE_PARAMS = i18n.translate('xpack.synthetics.monitorInspect.hideParams', const AS_JSON = i18n.translate('xpack.synthetics.monitorInspect.asJson', { defaultMessage: 'As JSON', }); + +const LINKED_POLICIES_LABEL = i18n.translate( + 'xpack.synthetics.monitorInspect.linkedPoliciesLabel', + { + defaultMessage: 'Linked Fleet policies', + } +); + +const PRIVATE_LOCATION_LABEL = i18n.translate( + 'xpack.synthetics.monitorInspect.privateLocationLabel', + { + defaultMessage: 'Private location', + } +); + +const AGENT_POLICY_LABEL = i18n.translate('xpack.synthetics.monitorInspect.agentPolicyLabel', { + defaultMessage: 'Agent policy', +}); + +const PACKAGE_POLICY_LABEL = i18n.translate('xpack.synthetics.monitorInspect.packagePolicyLabel', { + defaultMessage: 'Package policy', +}); + +const MISSING_REFERENCES_TITLE = i18n.translate( + 'xpack.synthetics.monitorInspect.missingReferencesTitle', + { + defaultMessage: 'Package policy references not found', + } +); + +const MISSING_REFERENCES_DESCRIPTION = i18n.translate( + 'xpack.synthetics.monitorInspect.missingReferencesDescription', + { + defaultMessage: + 'This monitor has private locations but no saved references to package policies. Save the monitor to populate the references.', + } +); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/index.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/index.tsx index 63744417d4233..47232410c1633 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/index.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/index.tsx @@ -52,7 +52,11 @@ export const MonitorSteps = ({ )} - + ); }; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/inspect_monitor_portal.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/inspect_monitor_portal.tsx index 31d0344b7ceb7..c73faf94bcda6 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/inspect_monitor_portal.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/inspect_monitor_portal.tsx @@ -14,13 +14,15 @@ import { InspectMonitorPortalNode } from '../portals'; export const InspectMonitorPortal = ({ isValid, monitorFields, + isEditFlow = false, }: { isValid: boolean; monitorFields: SyntheticsMonitor; + isEditFlow?: boolean; }) => { return ( - + ); }; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts index 928d222aedf07..ea0535e273707 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts @@ -29,18 +29,32 @@ export const createMonitorAPI = async ({ }); }; +export interface PackagePolicyLink { + locationId: string; + locationLabel: string; + agentPolicyId: string; + packagePolicyId: string; +} + export interface MonitorInspectResponse { publicConfigs: any[]; privateConfig: PackagePolicy | null; } +export interface InspectMonitorAPIResponse { + result: MonitorInspectResponse; + decodedCode: string; + packagePolicyLinks: PackagePolicyLink[]; + hasMissingReferences: boolean; +} + export const inspectMonitorAPI = async ({ monitor, hideParams, }: { hideParams?: boolean; monitor: SyntheticsMonitor | EncryptedSyntheticsMonitor; -}): Promise<{ result: MonitorInspectResponse; decodedCode: string }> => { +}): Promise => { return await apiService.post(SYNTHETICS_API_URLS.SYNTHETICS_MONITOR_INSPECT, monitor, undefined, { hideParams, }); diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts new file mode 100644 index 0000000000000..ab36a88455419 --- /dev/null +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts @@ -0,0 +1,180 @@ +/* + * 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 type { PrivateLocationAttributes } from '../../runtime_types/private_locations'; +import { buildPackagePolicyLinks } from './inspect_monitor'; + +const makePrivateLocation = ( + overrides: Partial = {} +): PrivateLocationAttributes => ({ + id: 'loc-1', + label: 'My Private Location', + agentPolicyId: 'agent-policy-1', + isServiceManaged: false, + ...overrides, +}); + +const makeMockRepository = ( + references: Array<{ id: string; name: string; type: string }> = [] +) => ({ + get: jest.fn().mockResolvedValue({ references }), +}); + +describe('buildPackagePolicyLinks', () => { + it('returns empty links when monitorId is undefined (new monitor)', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: undefined, + monitorPrivateLocations: [{ id: 'loc-1', label: 'Loc 1' }], + privateLocations: [makePrivateLocation()], + monitorConfigRepository: makeMockRepository(), + }); + + expect(result).toEqual({ packagePolicyLinks: [], hasMissingReferences: false }); + }); + + it('returns empty links when there are no private locations', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [], + privateLocations: [makePrivateLocation()], + monitorConfigRepository: makeMockRepository(), + }); + + expect(result).toEqual({ packagePolicyLinks: [], hasMissingReferences: false }); + }); + + it('builds links with references present', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], + privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], + monitorConfigRepository: makeMockRepository([ + { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, + ]), + }); + + expect(result.hasMissingReferences).toBe(false); + expect(result.packagePolicyLinks).toEqual([ + { + locationId: 'loc-1', + locationLabel: 'My Location', + agentPolicyId: 'ap-1', + packagePolicyId: 'monitor-1-loc-1', + }, + ]); + }); + + it('sets hasMissingReferences when references are empty', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], + privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], + monitorConfigRepository: makeMockRepository([]), + }); + + expect(result.hasMissingReferences).toBe(true); + expect(result.packagePolicyLinks).toHaveLength(1); + expect(result.packagePolicyLinks[0].packagePolicyId).toBe('monitor-1-loc-1'); + }); + + it('handles multiple private locations with partial references', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [ + { id: 'loc-1', label: 'Location 1' }, + { id: 'loc-2', label: 'Location 2' }, + ], + privateLocations: [ + makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1', label: 'Location 1' }), + makePrivateLocation({ id: 'loc-2', agentPolicyId: 'ap-2', label: 'Location 2' }), + ], + monitorConfigRepository: makeMockRepository([ + { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, + ]), + }); + + expect(result.hasMissingReferences).toBe(true); + expect(result.packagePolicyLinks).toHaveLength(2); + expect(result.packagePolicyLinks[0]).toEqual({ + locationId: 'loc-1', + locationLabel: 'Location 1', + agentPolicyId: 'ap-1', + packagePolicyId: 'monitor-1-loc-1', + }); + expect(result.packagePolicyLinks[1]).toEqual({ + locationId: 'loc-2', + locationLabel: 'Location 2', + agentPolicyId: 'ap-2', + packagePolicyId: 'monitor-1-loc-2', + }); + }); + + it('skips locations without a matching private location definition', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [ + { id: 'loc-1', label: 'Location 1' }, + { id: 'loc-unknown', label: 'Unknown' }, + ], + privateLocations: [ + makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1', label: 'Location 1' }), + ], + monitorConfigRepository: makeMockRepository([ + { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, + ]), + }); + + expect(result.packagePolicyLinks).toHaveLength(1); + expect(result.packagePolicyLinks[0].locationId).toBe('loc-1'); + }); + + it('falls back to locationId when label is empty', async () => { + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [{ id: 'loc-1', label: '' }], + privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], + monitorConfigRepository: makeMockRepository([ + { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, + ]), + }); + + expect(result.packagePolicyLinks[0].locationLabel).toBe('loc-1'); + }); + + it('handles monitorConfigRepository.get throwing (new monitor not saved yet)', async () => { + const mockRepo = { + get: jest.fn().mockRejectedValue(new Error('Not found')), + }; + + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], + privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], + monitorConfigRepository: mockRepo, + }); + + expect(result.hasMissingReferences).toBe(true); + expect(result.packagePolicyLinks).toHaveLength(1); + expect(result.packagePolicyLinks[0].packagePolicyId).toBe('monitor-1-loc-1'); + }); + + it('handles saved object with no references field', async () => { + const mockRepo = { + get: jest.fn().mockResolvedValue({}), + }; + + const result = await buildPackagePolicyLinks({ + monitorId: 'monitor-1', + monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], + privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], + monitorConfigRepository: mockRepo, + }); + + expect(result.hasMissingReferences).toBe(true); + expect(result.packagePolicyLinks).toHaveLength(1); + }); +}); diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts index 9447b2a72a297..a46c8fbe9ac18 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts @@ -6,6 +6,7 @@ */ import { v4 as uuidV4 } from 'uuid'; import { schema } from '@kbn/config-schema'; +import type { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; import type { PrivateLocationAttributes } from '../../runtime_types/private_locations'; import type { SyntheticsRestApiRouteFactory } from '../types'; import { unzipFile } from '../../common/unzip_project_code'; @@ -17,6 +18,13 @@ import { validateMonitor } from './monitor_validation'; import { getPrivateLocationsForMonitor } from './add_monitor/utils'; import { AddEditMonitorAPI } from './add_monitor/add_monitor_api'; +export interface PackagePolicyLink { + locationId: string; + locationLabel: string; + agentPolicyId: string; + packagePolicyId: string; +} + export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ method: 'POST', path: SYNTHETICS_API_URLS.SYNTHETICS_MONITOR_INSPECT, @@ -28,8 +36,15 @@ export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = }), }, handler: async (routeContext): Promise => { - const { savedObjectsClient, server, syntheticsMonitorClient, request, spaceId, response } = - routeContext; + const { + savedObjectsClient, + server, + syntheticsMonitorClient, + request, + spaceId, + response, + monitorConfigRepository, + } = routeContext; // usually id is auto generated, but this is useful for testing const { id, hideParams = true } = request.query; @@ -91,7 +106,26 @@ export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = decodedCode = await unzipFile(hasSourceContent); } - return response.ok({ body: { result, decodedCode: formatCode(decodedCode) } }); + const monitorId = normalizedMonitor.config_id; + const monitorPrivateLocations = normalizedMonitor[ConfigKey.LOCATIONS].filter( + (loc) => !loc.isServiceManaged + ); + + const { packagePolicyLinks, hasMissingReferences } = await buildPackagePolicyLinks({ + monitorId, + monitorPrivateLocations, + privateLocations, + monitorConfigRepository, + }); + + return response.ok({ + body: { + result, + decodedCode: formatCode(decodedCode), + packagePolicyLinks, + hasMissingReferences, + }, + }); } catch (error) { server.logger.error( `Unable to inspect Synthetics monitor ${monitorWithDefaults[ConfigKey.NAME]}`, @@ -106,6 +140,60 @@ export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = }, }); +export const buildPackagePolicyLinks = async ({ + monitorId, + monitorPrivateLocations, + privateLocations, + monitorConfigRepository, +}: { + monitorId?: string; + monitorPrivateLocations: Array<{ id: string; label?: string; isServiceManaged?: boolean }>; + privateLocations: PrivateLocationAttributes[]; + monitorConfigRepository: { + get: (id: string) => Promise<{ references?: SavedObjectReference[] }>; + }; +}): Promise<{ packagePolicyLinks: PackagePolicyLink[]; hasMissingReferences: boolean }> => { + if (!monitorId || monitorPrivateLocations.length === 0) { + return { packagePolicyLinks: [], hasMissingReferences: false }; + } + + let references: SavedObjectReference[] = []; + try { + const monitorSO = await monitorConfigRepository.get(monitorId); + references = monitorSO.references ?? []; + } catch { + // Monitor doesn't exist yet (new monitor) or can't be fetched + } + + const referenceIdSet = new Set(references.map((ref) => ref.id)); + + const links: PackagePolicyLink[] = []; + let hasMissingReferences = false; + + for (const loc of monitorPrivateLocations) { + const privateLocationDef = privateLocations.find((pl) => pl.id === loc.id); + if (!privateLocationDef) { + continue; + } + + const expectedPolicyId = `${monitorId}-${loc.id}`; + const hasReference = referenceIdSet.has(expectedPolicyId); + + if (!hasReference) { + hasMissingReferences = true; + } + + links.push({ + locationId: loc.id, + locationLabel: loc.label || loc.id, + agentPolicyId: privateLocationDef.agentPolicyId, + packagePolicyId: expectedPolicyId, + }); + } + + return { packagePolicyLinks: links, hasMissingReferences }; +}; + const formatCode = (code: string) => { const replacements = [ { pattern: /\(\d*,\s*import_synthetics\d*\.step\)/g, replacement: 'step' }, From 7b2df6514c97f23da0a6e05c1f15826d748b620a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Wed, 18 Mar 2026 13:46:56 +0000 Subject: [PATCH 2/9] pass down getPolicyId as param to avoid duplicating logic --- .../monitor_cruds/inspect_monitor.test.ts | 31 +++++++++++++++++++ .../routes/monitor_cruds/inspect_monitor.ts | 6 +++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts index ab36a88455419..dd8f156e75d88 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts @@ -24,6 +24,8 @@ const makeMockRepository = ( get: jest.fn().mockResolvedValue({ references }), }); +const mockGetPolicyId = (configId: string, locationId: string) => `${configId}-${locationId}`; + describe('buildPackagePolicyLinks', () => { it('returns empty links when monitorId is undefined (new monitor)', async () => { const result = await buildPackagePolicyLinks({ @@ -31,6 +33,7 @@ describe('buildPackagePolicyLinks', () => { monitorPrivateLocations: [{ id: 'loc-1', label: 'Loc 1' }], privateLocations: [makePrivateLocation()], monitorConfigRepository: makeMockRepository(), + getPolicyId: mockGetPolicyId, }); expect(result).toEqual({ packagePolicyLinks: [], hasMissingReferences: false }); @@ -42,6 +45,7 @@ describe('buildPackagePolicyLinks', () => { monitorPrivateLocations: [], privateLocations: [makePrivateLocation()], monitorConfigRepository: makeMockRepository(), + getPolicyId: mockGetPolicyId, }); expect(result).toEqual({ packagePolicyLinks: [], hasMissingReferences: false }); @@ -55,6 +59,7 @@ describe('buildPackagePolicyLinks', () => { monitorConfigRepository: makeMockRepository([ { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, ]), + getPolicyId: mockGetPolicyId, }); expect(result.hasMissingReferences).toBe(false); @@ -74,6 +79,7 @@ describe('buildPackagePolicyLinks', () => { monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], monitorConfigRepository: makeMockRepository([]), + getPolicyId: mockGetPolicyId, }); expect(result.hasMissingReferences).toBe(true); @@ -95,6 +101,7 @@ describe('buildPackagePolicyLinks', () => { monitorConfigRepository: makeMockRepository([ { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, ]), + getPolicyId: mockGetPolicyId, }); expect(result.hasMissingReferences).toBe(true); @@ -126,6 +133,7 @@ describe('buildPackagePolicyLinks', () => { monitorConfigRepository: makeMockRepository([ { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, ]), + getPolicyId: mockGetPolicyId, }); expect(result.packagePolicyLinks).toHaveLength(1); @@ -140,6 +148,7 @@ describe('buildPackagePolicyLinks', () => { monitorConfigRepository: makeMockRepository([ { id: 'monitor-1-loc-1', name: 'monitor-1-loc-1', type: 'ingest-package-policies' }, ]), + getPolicyId: mockGetPolicyId, }); expect(result.packagePolicyLinks[0].locationLabel).toBe('loc-1'); @@ -155,6 +164,7 @@ describe('buildPackagePolicyLinks', () => { monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], monitorConfigRepository: mockRepo, + getPolicyId: mockGetPolicyId, }); expect(result.hasMissingReferences).toBe(true); @@ -172,9 +182,30 @@ describe('buildPackagePolicyLinks', () => { monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], monitorConfigRepository: mockRepo, + getPolicyId: mockGetPolicyId, }); expect(result.hasMissingReferences).toBe(true); expect(result.packagePolicyLinks).toHaveLength(1); }); + + it('uses the provided getPolicyId function', async () => { + const customGetPolicyId = jest.fn( + (configId: string, locationId: string) => `custom-${configId}--${locationId}` + ); + + const result = await buildPackagePolicyLinks({ + monitorId: 'mon-1', + monitorPrivateLocations: [{ id: 'loc-1', label: 'Loc' }], + privateLocations: [makePrivateLocation({ id: 'loc-1', agentPolicyId: 'ap-1' })], + monitorConfigRepository: makeMockRepository([ + { id: 'custom-mon-1--loc-1', name: 'ref', type: 'ingest-package-policies' }, + ]), + getPolicyId: customGetPolicyId, + }); + + expect(customGetPolicyId).toHaveBeenCalledWith('mon-1', 'loc-1'); + expect(result.packagePolicyLinks[0].packagePolicyId).toBe('custom-mon-1--loc-1'); + expect(result.hasMissingReferences).toBe(false); + }); }); diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts index a46c8fbe9ac18..0bc59253df65f 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts @@ -116,6 +116,8 @@ export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () = monitorPrivateLocations, privateLocations, monitorConfigRepository, + getPolicyId: (configId, locationId) => + syntheticsMonitorClient.privateLocationAPI.getPolicyId({ id: configId }, locationId), }); return response.ok({ @@ -145,6 +147,7 @@ export const buildPackagePolicyLinks = async ({ monitorPrivateLocations, privateLocations, monitorConfigRepository, + getPolicyId, }: { monitorId?: string; monitorPrivateLocations: Array<{ id: string; label?: string; isServiceManaged?: boolean }>; @@ -152,6 +155,7 @@ export const buildPackagePolicyLinks = async ({ monitorConfigRepository: { get: (id: string) => Promise<{ references?: SavedObjectReference[] }>; }; + getPolicyId: (configId: string, locationId: string) => string; }): Promise<{ packagePolicyLinks: PackagePolicyLink[]; hasMissingReferences: boolean }> => { if (!monitorId || monitorPrivateLocations.length === 0) { return { packagePolicyLinks: [], hasMissingReferences: false }; @@ -176,7 +180,7 @@ export const buildPackagePolicyLinks = async ({ continue; } - const expectedPolicyId = `${monitorId}-${loc.id}`; + const expectedPolicyId = getPolicyId(monitorId, loc.id); const hasReference = referenceIdSet.has(expectedPolicyId); if (!hasReference) { From 16464d8b491b30c0576f0550601e42d23eb6a4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Wed, 18 Mar 2026 17:37:46 +0000 Subject: [PATCH 3/9] hide table when references are missing --- .../common/components/monitor_inspect.tsx | 71 ++++++++++++++++--- .../monitor_cruds/inspect_monitor.test.ts | 19 ++--- .../routes/monitor_cruds/inspect_monitor.ts | 1 + 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index 7fb2ee2cfeddc..39a23caf13474 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n'; import { EuiBasicTable, + EuiButtonEmpty, EuiCallOut, EuiFlyout, EuiButton, @@ -38,7 +39,8 @@ import type { MonitorInspectResponse, PackagePolicyLink, } from '../../../state/monitor_management/api'; -import { inspectMonitorAPI } from '../../../state/monitor_management/api'; +import { inspectMonitorAPI, updateMonitorAPI } from '../../../state/monitor_management/api'; +import { kibanaService } from '../../../../../utils/kibana_service'; interface InspectorProps { isValid: boolean; @@ -59,6 +61,7 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I }; const [isInspecting, setIsInspecting] = useState(false); + const [migrateCount, setMigrateCount] = useState(0); const onButtonClick = () => { setIsInspecting(() => !isInspecting); setIsFlyoutVisible(() => !isFlyoutVisible); @@ -74,7 +77,7 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I // FIXME: Dario couldn't find a solution for monitorFields // which is not memoized downstream // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isInspecting, hideParams]); + }, [isInspecting, hideParams, migrateCount]); let flyout; @@ -119,12 +122,14 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I {formatContent(data.result, asJson)} {data.decodedCode && } - {isEditFlow && data.packagePolicyLinks.length > 0 && ( + {isEditFlow && (data.packagePolicyLinks.length > 0 || data.hasMissingReferences) && ( <> setMigrateCount((c) => c + 1)} /> )} @@ -169,11 +174,34 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I const PackagePolicyLinksTable = ({ links, hasMissingReferences, + monitorFields, + onMigrateSuccess, }: { links: PackagePolicyLink[]; hasMissingReferences: boolean; + monitorFields: SyntheticsMonitor; + onMigrateSuccess: () => void; }) => { const { basePath } = useSyntheticsSettingsContext(); + const [isMigrating, setIsMigrating] = useState(false); + + const handleMigrate = async () => { + const monitorId = monitorFields.config_id; + if (!monitorId) return; + setIsMigrating(true); + try { + await updateMonitorAPI({ monitor: monitorFields, id: monitorId }); + kibanaService.toasts.addSuccess({ + title: MIGRATE_SUCCESS_LABEL, + toastLifeTimeMs: 3000, + }); + onMigrateSuccess(); + } catch (err) { + kibanaService.toasts.addError(err, { title: MIGRATE_FAILURE_LABEL }); + } finally { + setIsMigrating(false); + } + }; const columns: Array> = [ { @@ -227,16 +255,27 @@ const PackagePolicyLinksTable = ({ data-test-subj="syntheticsPackagePolicyMissingReferencesCallout" >

{MISSING_REFERENCES_DESCRIPTION}

+ + {MIGRATE_LABEL} + )} - + {links.length > 0 && ( + + )} ); }; @@ -348,3 +387,17 @@ const MISSING_REFERENCES_DESCRIPTION = i18n.translate( 'This monitor has private locations but no saved references to package policies. Save the monitor to populate the references.', } ); + +const MIGRATE_LABEL = i18n.translate('xpack.synthetics.monitorInspect.migrateLabel', { + defaultMessage: 'Migrate now', +}); + +const MIGRATE_SUCCESS_LABEL = i18n.translate( + 'xpack.synthetics.monitorInspect.migrateSuccessLabel', + { defaultMessage: 'Monitor policies migrated successfully' } +); + +const MIGRATE_FAILURE_LABEL = i18n.translate( + 'xpack.synthetics.monitorInspect.migrateFailureLabel', + { defaultMessage: 'Failed to migrate monitor policies' } +); diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts index dd8f156e75d88..4f104d4e581d7 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.test.ts @@ -73,7 +73,7 @@ describe('buildPackagePolicyLinks', () => { ]); }); - it('sets hasMissingReferences when references are empty', async () => { + it('sets hasMissingReferences and returns no links when references are empty', async () => { const result = await buildPackagePolicyLinks({ monitorId: 'monitor-1', monitorPrivateLocations: [{ id: 'loc-1', label: 'My Location' }], @@ -83,8 +83,7 @@ describe('buildPackagePolicyLinks', () => { }); expect(result.hasMissingReferences).toBe(true); - expect(result.packagePolicyLinks).toHaveLength(1); - expect(result.packagePolicyLinks[0].packagePolicyId).toBe('monitor-1-loc-1'); + expect(result.packagePolicyLinks).toHaveLength(0); }); it('handles multiple private locations with partial references', async () => { @@ -105,19 +104,14 @@ describe('buildPackagePolicyLinks', () => { }); expect(result.hasMissingReferences).toBe(true); - expect(result.packagePolicyLinks).toHaveLength(2); + // Only loc-1 has a reference; loc-2 is missing so it's excluded from links + expect(result.packagePolicyLinks).toHaveLength(1); expect(result.packagePolicyLinks[0]).toEqual({ locationId: 'loc-1', locationLabel: 'Location 1', agentPolicyId: 'ap-1', packagePolicyId: 'monitor-1-loc-1', }); - expect(result.packagePolicyLinks[1]).toEqual({ - locationId: 'loc-2', - locationLabel: 'Location 2', - agentPolicyId: 'ap-2', - packagePolicyId: 'monitor-1-loc-2', - }); }); it('skips locations without a matching private location definition', async () => { @@ -168,8 +162,7 @@ describe('buildPackagePolicyLinks', () => { }); expect(result.hasMissingReferences).toBe(true); - expect(result.packagePolicyLinks).toHaveLength(1); - expect(result.packagePolicyLinks[0].packagePolicyId).toBe('monitor-1-loc-1'); + expect(result.packagePolicyLinks).toHaveLength(0); }); it('handles saved object with no references field', async () => { @@ -186,7 +179,7 @@ describe('buildPackagePolicyLinks', () => { }); expect(result.hasMissingReferences).toBe(true); - expect(result.packagePolicyLinks).toHaveLength(1); + expect(result.packagePolicyLinks).toHaveLength(0); }); it('uses the provided getPolicyId function', async () => { diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts index 0bc59253df65f..22aa7f7ac3451 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts @@ -185,6 +185,7 @@ export const buildPackagePolicyLinks = async ({ if (!hasReference) { hasMissingReferences = true; + continue; } links.push({ From c12b25b6ed8d8dc2b8026fe961cb5d071a3bbc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Wed, 18 Mar 2026 17:58:51 +0000 Subject: [PATCH 4/9] use saved fields when migrating policies --- .../components/common/components/monitor_inspect.tsx | 11 ++++++++--- .../apps/synthetics/state/monitor_management/api.ts | 6 ++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index 39a23caf13474..2ea55449d770b 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -39,7 +39,11 @@ import type { MonitorInspectResponse, PackagePolicyLink, } from '../../../state/monitor_management/api'; -import { inspectMonitorAPI, updateMonitorAPI } from '../../../state/monitor_management/api'; +import { + fetchMonitorAPI, + inspectMonitorAPI, + updateMonitorAPI, +} from '../../../state/monitor_management/api'; import { kibanaService } from '../../../../../utils/kibana_service'; interface InspectorProps { @@ -190,7 +194,8 @@ const PackagePolicyLinksTable = ({ if (!monitorId) return; setIsMigrating(true); try { - await updateMonitorAPI({ monitor: monitorFields, id: monitorId }); + const savedMonitor = await fetchMonitorAPI({ id: monitorId }); + await updateMonitorAPI({ monitor: savedMonitor, id: monitorId }); kibanaService.toasts.addSuccess({ title: MIGRATE_SUCCESS_LABEL, toastLifeTimeMs: 3000, @@ -384,7 +389,7 @@ const MISSING_REFERENCES_DESCRIPTION = i18n.translate( 'xpack.synthetics.monitorInspect.missingReferencesDescription', { defaultMessage: - 'This monitor has private locations but no saved references to package policies. Save the monitor to populate the references.', + 'This monitor has package policies that use a legacy ID format. Click Migrate now to update them.', } ); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts index ea0535e273707..df8d5b8f8dea3 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts @@ -60,6 +60,12 @@ export const inspectMonitorAPI = async ({ }); }; +export const fetchMonitorAPI = async ({ id }: { id: string }): Promise => { + return await apiService.get( + SYNTHETICS_API_URLS.GET_SYNTHETICS_MONITOR.replace('{monitorId}', id) + ); +}; + export const updateMonitorAPI = async ({ monitor, id, From f3cb9307489129a01390b87d1280c78ea6c4696d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Thu, 19 Mar 2026 13:26:35 +0000 Subject: [PATCH 5/9] fix integration tests --- .../apis/synthetics/inspect_monitor.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts index 8f5f12df2d975..c3f24f354190c 100644 --- a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts +++ b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/synthetics/inspect_monitor.ts @@ -74,6 +74,8 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { ); rawExpect(apiResponse).toEqual({ + hasMissingReferences: false, + packagePolicyLinks: [], result: { publicConfigs: [ rawExpect.objectContaining({ @@ -147,6 +149,8 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { ], }); rawExpect(apiResponse).toEqual({ + hasMissingReferences: false, + packagePolicyLinks: [], result: { publicConfigs: [ rawExpect.objectContaining({ From 1ad6560e3a547dde41956fb1286456035aac7ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Thu, 19 Mar 2026 17:13:39 +0000 Subject: [PATCH 6/9] move PackagePolicyLink to common --- .../plugins/synthetics/common/types/synthetics_monitor.ts | 7 +++++++ .../apps/synthetics/state/monitor_management/api.ts | 8 +------- .../server/routes/monitor_cruds/inspect_monitor.ts | 7 +------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts index 6989b54568c76..49f4f14e94bc8 100644 --- a/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts @@ -20,3 +20,10 @@ export interface AgentPolicyInfo { description?: string; namespace?: string; } + +export interface PackagePolicyLink { + locationId: string; + locationLabel: string; + agentPolicyId: string; + packagePolicyId: string; +} \ No newline at end of file diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts index df8d5b8f8dea3..78699fa01df90 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts @@ -15,6 +15,7 @@ import type { SyntheticsMonitorWithId, } from '../../../../../common/runtime_types'; import { INITIAL_REST_VERSION, SYNTHETICS_API_URLS } from '../../../../../common/constants'; +import type { PackagePolicyLink } from '../../../../../common/types'; export type UpsertMonitorResponse = ServiceLocationErrorsResponse | SyntheticsMonitorWithId; @@ -29,13 +30,6 @@ export const createMonitorAPI = async ({ }); }; -export interface PackagePolicyLink { - locationId: string; - locationLabel: string; - agentPolicyId: string; - packagePolicyId: string; -} - export interface MonitorInspectResponse { publicConfigs: any[]; privateConfig: PackagePolicy | null; diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts index 22aa7f7ac3451..52a6af1235e58 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts @@ -17,13 +17,8 @@ import { DEFAULT_FIELDS } from '../../../common/constants/monitor_defaults'; import { validateMonitor } from './monitor_validation'; import { getPrivateLocationsForMonitor } from './add_monitor/utils'; import { AddEditMonitorAPI } from './add_monitor/add_monitor_api'; +import type { PackagePolicyLink } from '../../../common/types'; -export interface PackagePolicyLink { - locationId: string; - locationLabel: string; - agentPolicyId: string; - packagePolicyId: string; -} export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ method: 'POST', From bc2b634ddf682e37cdd92fa577ce3c52b2f696da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Thu, 19 Mar 2026 18:28:19 +0000 Subject: [PATCH 7/9] strip server fields before putting --- .../components/common/components/monitor_inspect.tsx | 7 +++++-- .../public/apps/synthetics/state/monitor_management/api.ts | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index 2ea55449d770b..e4c1b07456698 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -33,7 +33,7 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import yaml from 'js-yaml'; import { useSyntheticsSettingsContext } from '../../../contexts'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; -import type { SyntheticsMonitor } from '../../../../../../common/runtime_types'; +import type { SyntheticsMonitor, SyntheticsMonitorWithId } from '../../../../../../common/runtime_types'; import { MonitorTypeEnum } from '../../../../../../common/runtime_types'; import type { MonitorInspectResponse, @@ -175,6 +175,9 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I ); }; +const stripServerFields = ({ created_at: _ca, updated_at: _ua, spaceId: _si, ...savedMonitor }: SyntheticsMonitorWithId & { spaceId?: string }): SyntheticsMonitor => + savedMonitor; + const PackagePolicyLinksTable = ({ links, hasMissingReferences, @@ -194,7 +197,7 @@ const PackagePolicyLinksTable = ({ if (!monitorId) return; setIsMigrating(true); try { - const savedMonitor = await fetchMonitorAPI({ id: monitorId }); + const savedMonitor = stripServerFields(await fetchMonitorAPI({ id: monitorId })); await updateMonitorAPI({ monitor: savedMonitor, id: monitorId }); kibanaService.toasts.addSuccess({ title: MIGRATE_SUCCESS_LABEL, diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts index 78699fa01df90..60182b45a1495 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts @@ -16,6 +16,7 @@ import type { } from '../../../../../common/runtime_types'; import { INITIAL_REST_VERSION, SYNTHETICS_API_URLS } from '../../../../../common/constants'; import type { PackagePolicyLink } from '../../../../../common/types'; +export type { PackagePolicyLink }; export type UpsertMonitorResponse = ServiceLocationErrorsResponse | SyntheticsMonitorWithId; @@ -54,8 +55,8 @@ export const inspectMonitorAPI = async ({ }); }; -export const fetchMonitorAPI = async ({ id }: { id: string }): Promise => { - return await apiService.get( +export const fetchMonitorAPI = async ({ id }: { id: string }): Promise => { + return await apiService.get( SYNTHETICS_API_URLS.GET_SYNTHETICS_MONITOR.replace('{monitorId}', id) ); }; From cf04cb4b1159f45fe4da7b7f4dda472fd6a3574a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:36:56 +0000 Subject: [PATCH 8/9] Changes from node scripts/eslint_all_files --no-cache --fix --- .../synthetics/common/types/synthetics_monitor.ts | 2 +- .../common/components/monitor_inspect.tsx | 13 ++++++++++--- .../server/routes/monitor_cruds/inspect_monitor.ts | 1 - 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts index 49f4f14e94bc8..ae476c2b31581 100644 --- a/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/common/types/synthetics_monitor.ts @@ -26,4 +26,4 @@ export interface PackagePolicyLink { locationLabel: string; agentPolicyId: string; packagePolicyId: string; -} \ No newline at end of file +} diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index e4c1b07456698..11979ca1a1b99 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -33,7 +33,10 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import yaml from 'js-yaml'; import { useSyntheticsSettingsContext } from '../../../contexts'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; -import type { SyntheticsMonitor, SyntheticsMonitorWithId } from '../../../../../../common/runtime_types'; +import type { + SyntheticsMonitor, + SyntheticsMonitorWithId, +} from '../../../../../../common/runtime_types'; import { MonitorTypeEnum } from '../../../../../../common/runtime_types'; import type { MonitorInspectResponse, @@ -175,8 +178,12 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I ); }; -const stripServerFields = ({ created_at: _ca, updated_at: _ua, spaceId: _si, ...savedMonitor }: SyntheticsMonitorWithId & { spaceId?: string }): SyntheticsMonitor => - savedMonitor; +const stripServerFields = ({ + created_at: _ca, + updated_at: _ua, + spaceId: _si, + ...savedMonitor +}: SyntheticsMonitorWithId & { spaceId?: string }): SyntheticsMonitor => savedMonitor; const PackagePolicyLinksTable = ({ links, diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts index 52a6af1235e58..3f3325a79c283 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/inspect_monitor.ts @@ -19,7 +19,6 @@ import { getPrivateLocationsForMonitor } from './add_monitor/utils'; import { AddEditMonitorAPI } from './add_monitor/add_monitor_api'; import type { PackagePolicyLink } from '../../../common/types'; - export const inspectSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({ method: 'POST', path: SYNTHETICS_API_URLS.SYNTHETICS_MONITOR_INSPECT, From ce5fd5f3da33b21d0164441d20b99d5fc25edfa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn?= Date: Fri, 20 Mar 2026 10:46:52 +0000 Subject: [PATCH 9/9] fix typing --- .../monitor_management/monitor_types.ts | 1 + .../common/components/monitor_inspect.tsx | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index 6be951534a4d5..3db59141f97c6 100644 --- a/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -345,6 +345,7 @@ export const EncryptedSyntheticsMonitorCodec = t.union([ export const SyntheticsMonitorWithIdCodec = t.intersection([ SyntheticsMonitorCodec, t.interface({ id: t.string, updated_at: t.string, created_at: t.string }), + t.partial({ spaces: t.array(t.string), spaceId: t.string, revision: t.number }), ]); const HeartbeatFieldsCodec = t.intersection([ diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index e4c1b07456698..a0c6cc73e680e 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -33,7 +33,10 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import yaml from 'js-yaml'; import { useSyntheticsSettingsContext } from '../../../contexts'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; -import type { SyntheticsMonitor, SyntheticsMonitorWithId } from '../../../../../../common/runtime_types'; +import type { + SyntheticsMonitor, + SyntheticsMonitorWithId, +} from '../../../../../../common/runtime_types'; import { MonitorTypeEnum } from '../../../../../../common/runtime_types'; import type { MonitorInspectResponse, @@ -175,8 +178,13 @@ export const MonitorInspect = ({ isValid, monitorFields, isEditFlow = false }: I ); }; -const stripServerFields = ({ created_at: _ca, updated_at: _ua, spaceId: _si, ...savedMonitor }: SyntheticsMonitorWithId & { spaceId?: string }): SyntheticsMonitor => - savedMonitor; +const stripServerFields = ({ + created_at: _ca, + updated_at: _ua, + spaceId: _si, + revision: _rev, + ...savedMonitor +}: SyntheticsMonitorWithId): SyntheticsMonitor => savedMonitor; const PackagePolicyLinksTable = ({ links,