From 8817351cc7d425fea3a704cbbb3884c576b6217a Mon Sep 17 00:00:00 2001 From: Francesco Fagnani Date: Wed, 26 Mar 2025 17:08:25 +0100 Subject: [PATCH] [Synthetics] Fix location filter in status rule executor (#215514) This PR closes #215505 by fixing the location filter when creating a custom status rule for monitors. https://github.com/user-attachments/assets/623b21fb-af45-42ae-a120-e38562451062 --------- Co-authored-by: Shahzad (cherry picked from commit 04d53ec134272fe111bf5553a0f1622418f75f71) # Conflicts: # x-pack/solutions/observability/plugins/synthetics/server/routes/common.ts --- .../components/alerts/status_rule_viz.tsx | 34 +++++---- .../status_rule/status_rule_executor.ts | 27 +++++-- .../synthetics/server/routes/common.test.ts | 2 +- .../synthetics/server/routes/common.ts | 73 ++++++++++--------- .../overview_status_service.test.ts | 6 +- .../overview_status_service.ts | 20 ++--- .../server/routes/suggestions/route.ts | 10 +-- 7 files changed, 92 insertions(+), 80 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/alerts/status_rule_viz.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/alerts/status_rule_viz.tsx index ddb65e22201cc..ab0f555f09978 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/alerts/status_rule_viz.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/alerts/status_rule_viz.tsx @@ -11,6 +11,7 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, + EuiLoadingSpinner, EuiPopover, EuiPopoverTitle, EuiSpacer, @@ -31,7 +32,7 @@ export const StatusRuleViz = ({ }: { ruleParams: StatusRuleParamsProps['ruleParams']; }) => { - const { data } = useSelector(selectInspectStatusRule); + const { data, loading } = useSelector(selectInspectStatusRule); const dispatch = useDispatch(); const { services: { inspector }, @@ -57,7 +58,7 @@ export const StatusRuleViz = ({ return ( - + {i18n.translate('xpack.synthetics.statusRuleViz.ruleAppliesToFlexItemLabel', { defaultMessage: 'Rule applies to ', @@ -68,17 +69,19 @@ export const StatusRuleViz = ({ isOpen={isPopoverOpen} closePopover={() => setIsPopoverOpen(false)} button={ - setIsPopoverOpen(!isPopoverOpen)} - > - {i18n.translate('xpack.synthetics.statusRuleViz.monitorQueryIdsPopoverButton', { - defaultMessage: - '{total} existing {total, plural, one {monitor} other {monitors}}', - values: { total: data?.monitors.length }, - })} - + loading ? undefined : ( + setIsPopoverOpen(!isPopoverOpen)} + > + {i18n.translate('xpack.synthetics.statusRuleViz.monitorQueryIdsPopoverButton', { + defaultMessage: + '{total} existing {total, plural, one {monitor} other {monitors}}', + values: { total: data?.monitors.length }, + })} + + ) } > @@ -93,6 +96,11 @@ export const StatusRuleViz = ({ + {loading && ( + + + + )} {/* to push detail button to end*/} diff --git a/x-pack/solutions/observability/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts b/x-pack/solutions/observability/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts index 24ab7c4dfb06e..30194566ae143 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts @@ -35,7 +35,7 @@ import { getUngroupedReasonMessage, } from './message_utils'; import { queryMonitorStatusAlert } from './queries/query_monitor_status_alert'; -import { parseArrayFilters } from '../../routes/common'; +import { parseArrayFilters, parseLocationFilter } from '../../routes/common'; import { SyntheticsServerSetup } from '../../types'; import { SyntheticsEsClient } from '../../lib'; import { @@ -115,14 +115,23 @@ export class StatusRuleExecutor { return processMonitors([]); } + const locationIds = await parseLocationFilter( + { + savedObjectsClient: this.soClient, + server: this.server, + syntheticsMonitorClient: this.syntheticsMonitorClient, + }, + this.params.locations + ); + const { filtersStr } = parseArrayFilters({ configIds, filter: baseFilter, - tags: this.params?.tags, - locations: this.params?.locations, - monitorTypes: this.params?.monitorTypes, - monitorQueryIds: this.params?.monitorIds, - projects: this.params?.projects, + tags: this.params.tags, + locations: locationIds, + monitorTypes: this.params.monitorTypes, + monitorQueryIds: this.params.monitorIds, + projects: this.params.projects, }); this.monitors = await getAllMonitors({ @@ -130,7 +139,11 @@ export class StatusRuleExecutor { filter: filtersStr, }); - this.debug(`Found ${this.monitors.length} monitors for params ${JSON.stringify(this.params)}`); + this.debug( + `Found ${this.monitors.length} monitors for params ${JSON.stringify( + this.params + )} | parsed location filter is ${JSON.stringify(locationIds)} ` + ); return processMonitors(this.monitors); } diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/common.test.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/common.test.ts index 7e5c0d610e520..ea981697db2e2 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/common.test.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/common.test.ts @@ -37,7 +37,7 @@ describe('common utils', () => { schedules: ['schedule1', 'schedule2'], }); expect(filters.filtersStr).toMatchInlineSnapshot( - `"synthetics-monitor.attributes.tags:(\\"tag1\\" OR \\"tag2\\") AND synthetics-monitor.attributes.project_id:(\\"project1\\" OR \\"project2\\") AND synthetics-monitor.attributes.type:(\\"type1\\" OR \\"type2\\") AND synthetics-monitor.attributes.schedule.number:(\\"schedule1\\" OR \\"schedule2\\") AND synthetics-monitor.attributes.id:(\\"query1\\" OR \\"query2\\") AND synthetics-monitor.attributes.config_id:(\\"1\\" OR \\"2\\")"` + `"synthetics-monitor.attributes.tags:(\\"tag1\\" OR \\"tag2\\") AND synthetics-monitor.attributes.project_id:(\\"project1\\" OR \\"project2\\") AND synthetics-monitor.attributes.type:(\\"type1\\" OR \\"type2\\") AND synthetics-monitor.attributes.locations.id:(\\"loc1\\" OR \\"loc2\\") AND synthetics-monitor.attributes.schedule.number:(\\"schedule1\\" OR \\"schedule2\\") AND synthetics-monitor.attributes.id:(\\"query1\\" OR \\"query2\\") AND synthetics-monitor.attributes.config_id:(\\"1\\" OR \\"2\\")"` ); }); }); diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/common.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/common.ts index 2a906f3cf6a4d..a36eec4043640 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/common.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/common.ts @@ -82,27 +82,11 @@ export const getMonitors = async ( sortField, sortOrder, query, - tags, - monitorTypes, - locations, - filter = '', searchAfter, - projects, - schedules, - monitorQueryIds, showFromAllSpaces, } = context.request.query; - const { filtersStr } = await getMonitorFilters({ - filter, - monitorTypes, - tags, - locations, - projects, - schedules, - monitorQueryIds, - context, - }); + const { filtersStr } = await getMonitorFilters(context); return context.savedObjectsClient.find({ type: syntheticsMonitorType, @@ -127,19 +111,29 @@ interface Filters { projects?: string | string[]; schedules?: string | string[]; monitorQueryIds?: string | string[]; + configIds?: string | string[]; } -export const getMonitorFilters = async ( - data: { - context: RouteContext; - } & Filters -) => { - const { context, locations } = data; - const locationFilter = await parseLocationFilter(context, locations); +export const getMonitorFilters = async (context: RouteContext) => { + const { + tags, + monitorTypes, + filter = '', + projects, + schedules, + monitorQueryIds, + locations: queryLocations, + } = context.request.query; + const locations = await parseLocationFilter(context, queryLocations); return parseArrayFilters({ - ...data, - locationFilter, + filter, + tags, + monitorTypes, + projects, + schedules, + monitorQueryIds, + locations, }); }; @@ -151,17 +145,14 @@ export const parseArrayFilters = ({ monitorTypes, schedules, monitorQueryIds, - locationFilter, -}: Filters & { - locationFilter?: string | string[]; - configIds?: string[]; -}) => { + locations, +}: Filters) => { const filtersStr = [ filter, getSavedObjectKqlFilter({ field: 'tags', values: tags }), getSavedObjectKqlFilter({ field: 'project_id', values: projects }), getSavedObjectKqlFilter({ field: 'type', values: monitorTypes }), - getSavedObjectKqlFilter({ field: 'locations.id', values: locationFilter }), + getSavedObjectKqlFilter({ field: 'locations.id', values: locations }), getSavedObjectKqlFilter({ field: 'schedule.number', values: schedules }), getSavedObjectKqlFilter({ field: 'id', values: monitorQueryIds }), getSavedObjectKqlFilter({ field: 'config_id', values: configIds }), @@ -169,7 +160,7 @@ export const parseArrayFilters = ({ .filter((f) => !!f) .join(' AND '); - return { filtersStr, locationFilter }; + return { filtersStr, locationIds: locations }; }; export const getSavedObjectKqlFilter = ({ @@ -206,12 +197,24 @@ export const getSavedObjectKqlFilter = ({ return `${fieldKey}:"${escapeQuotes(values)}"`; }; -const parseLocationFilter = async (context: RouteContext, locations?: string | string[]) => { +export const parseLocationFilter = async ( + { + syntheticsMonitorClient, + savedObjectsClient, + server, + }: Pick, + locations?: string | string[] +) => { if (!locations || locations?.length === 0) { return; } - const { allLocations } = await getAllLocations(context); + const { allLocations } = await getAllLocations({ + syntheticsMonitorClient, + savedObjectsClient, + server, + excludeAgentPolicies: true, + }); if (Array.isArray(locations)) { return locations diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.test.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.test.ts index 677f499b2a722..38c196488a152 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.test.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.test.ts @@ -158,7 +158,7 @@ describe('current status route', () => { }) ); const routeContext: any = { - request: {}, + request: { query: {} }, syntheticsEsClient, }; @@ -316,7 +316,7 @@ describe('current status route', () => { ); const routeContext: any = { - request: {}, + request: { query: {} }, syntheticsEsClient, }; @@ -420,7 +420,7 @@ describe('current status route', () => { }) ); const routeContext: any = { - request: {}, + request: { query: {} }, syntheticsEsClient, }; diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.ts index 5ff8a870db9de..e7dd4bdc72be0 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/overview_status/overview_status_service.ts @@ -39,7 +39,7 @@ export const SUMMARIES_PAGE_SIZE = 5000; export class OverviewStatusService { filterData: { - locationFilter?: string[] | string; + locationIds?: string[] | string; filtersStr?: string; } = {}; constructor( @@ -47,13 +47,7 @@ export class OverviewStatusService { ) {} async getOverviewStatus() { - const { request } = this.routeContext; - const queryParams = request.query as OverviewStatusQuery; - - this.filterData = await getMonitorFilters({ - ...queryParams, - context: this.routeContext, - }); + this.filterData = await getMonitorFilters(this.routeContext); const [allConfigs, statusResult] = await Promise.all([ this.getMonitorConfigs(), @@ -70,7 +64,7 @@ export class OverviewStatusService { disabledCount, disabledMonitorsCount, projectMonitorsCount, - } = processMonitors(allConfigs, this.filterData?.locationFilter); + } = processMonitors(allConfigs, this.filterData?.locationIds); return { allIds, @@ -100,7 +94,7 @@ export class OverviewStatusService { projects, showFromAllSpaces, } = params; - const { locationFilter } = this.filterData; + const { locationIds } = this.filterData; const getTermFilter = (field: string, value: string | string[] | undefined) => { if (!value || isEmpty(value)) { return []; @@ -129,10 +123,10 @@ export class OverviewStatusService { ...getTermFilter('monitor.project.id', projects), ]; - if (scopeStatusByLocation && !isEmpty(locationFilter) && locationFilter) { + if (scopeStatusByLocation && !isEmpty(locationIds) && locationIds) { filters.push({ terms: { - 'observer.name': locationFilter, + 'observer.name': locationIds, }, }); } @@ -242,7 +236,7 @@ export class OverviewStatusService { const enabledMonitors = monitors.filter((monitor) => monitor.attributes[ConfigKey.ENABLED]); const disabledMonitors = monitors.filter((monitor) => !monitor.attributes[ConfigKey.ENABLED]); - const queryLocIds = this.filterData?.locationFilter; + const queryLocIds = this.filterData?.locationIds; disabledMonitors.forEach((monitor) => { const monitorQueryId = monitor.attributes[ConfigKey.MONITOR_QUERY_ID]; diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/suggestions/route.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/suggestions/route.ts index 85ab73eb10a6e..d97b8ac9b9f35 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/suggestions/route.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/suggestions/route.ts @@ -65,15 +65,9 @@ export const getSyntheticsSuggestionsRoute: SyntheticsRestApiRouteFactory< savedObjectsClient, server: { logger }, } = route; - const { tags, locations, projects, monitorQueryIds, query } = route.request.query; + const { query } = route.request.query; - const { filtersStr } = await getMonitorFilters({ - tags, - locations, - projects, - monitorQueryIds, - context: route, - }); + const { filtersStr } = await getMonitorFilters(route); const { allLocations = [] } = await getAllLocations(route); try { const data = await savedObjectsClient.find({