[8.x] [Synthetics] Fix overview status empty state !! (#193898) (#193935)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Synthetics] Fix overview status empty state !!
(#193898)](https://github.com/elastic/kibana/pull/193898)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"Shahzad","email":"shahzad31comp@gmail.com"},"sourceCommit":{"committedDate":"2024-09-25T06:10:56Z","message":"[Synthetics]
Fix overview status empty state !! (#193898)\n\n## Summary\r\n\r\nFix
overview status empty state !!\r\n\r\nFixes
https://github.com/elastic/kibana/issues/193603\r\n\r\n---------\r\n\r\nCo-authored-by:
Dominique Belcher
<dominique.clarke@elastic.co>","sha":"7f6982789b66a5b014b4efd06e83e5282b97e0e3","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-management"],"title":"[Synthetics]
Fix overview status empty state
!!","number":193898,"url":"https://github.com/elastic/kibana/pull/193898","mergeCommit":{"message":"[Synthetics]
Fix overview status empty state !! (#193898)\n\n## Summary\r\n\r\nFix
overview status empty state !!\r\n\r\nFixes
https://github.com/elastic/kibana/issues/193603\r\n\r\n---------\r\n\r\nCo-authored-by:
Dominique Belcher
<dominique.clarke@elastic.co>","sha":"7f6982789b66a5b014b4efd06e83e5282b97e0e3"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193898","number":193898,"mergeCommit":{"message":"[Synthetics]
Fix overview status empty state !! (#193898)\n\n## Summary\r\n\r\nFix
overview status empty state !!\r\n\r\nFixes
https://github.com/elastic/kibana/issues/193603\r\n\r\n---------\r\n\r\nCo-authored-by:
Dominique Belcher
<dominique.clarke@elastic.co>","sha":"7f6982789b66a5b014b4efd06e83e5282b97e0e3"}}]}]
BACKPORT-->

Co-authored-by: Shahzad <shahzad31comp@gmail.com>
This commit is contained in:
Kibana Machine 2024-09-25 18:03:21 +10:00 committed by GitHub
parent 1524b53bb6
commit 93fcb9bc54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 121 additions and 84 deletions

View file

@ -78,9 +78,9 @@ export const PublicLocationsCodec = t.array(PublicLocationCodec);
export const MonitorServiceLocationCodec = t.intersection([
t.interface({
id: t.string,
label: t.string,
}),
t.partial({
label: t.string,
geo: LocationGeoCodec,
url: t.string,
isServiceManaged: t.boolean,

View file

@ -63,6 +63,7 @@ export const OverviewStatusCodec = t.interface({
upConfigs: t.record(t.string, OverviewStatusMetaDataCodec),
downConfigs: t.record(t.string, OverviewStatusMetaDataCodec),
pendingConfigs: t.record(t.string, OverviewStatusMetaDataCodec),
disabledConfigs: t.record(t.string, OverviewStatusMetaDataCodec),
enabledMonitorQueryIds: t.array(t.string),
disabledMonitorQueryIds: t.array(t.string),
allIds: t.array(t.string),

View file

@ -27,6 +27,7 @@ describe('defaults', () => {
{
id: 'us_central',
isServiceManaged: true,
label: 'US Central',
},
],
name: 'Browser monitor',
@ -89,6 +90,7 @@ describe('defaults', () => {
{
id: 'us_central',
isServiceManaged: true,
label: 'US Central',
},
],
name: 'Browser monitor',

View file

@ -14,6 +14,7 @@ import {
SyntheticsMonitor,
BrowserFields,
HTTPFields,
ScheduleUnit,
} from '../types';
export const getDefaultFormFields = (
@ -61,15 +62,15 @@ export const formatDefaultFormValues = (monitor?: SyntheticsMonitor) => {
let formMonitorType = monitor[ConfigKey.FORM_MONITOR_TYPE];
const monitorType = monitor[ConfigKey.MONITOR_TYPE];
let schedule = monitor[ConfigKey.SCHEDULE];
if (schedule?.unit === 's') {
schedule = { number: `${schedule.number}s`, unit: 's' as ScheduleUnit };
}
const monitorWithFormMonitorType = {
...monitor,
[ConfigKey.SCHEDULE]: schedule,
};
const schedule = monitor[ConfigKey.SCHEDULE];
if (schedule?.unit === 's') {
schedule.number = `${schedule.number}s`;
}
const params = monitorWithFormMonitorType[ConfigKey.PARAMS];
if (typeof params !== 'string' && params) {
try {

View file

@ -53,6 +53,7 @@ export const overviewStatusReducer = createReducer(initialState, (builder) => {
...action.payload.upConfigs,
...action.payload.downConfigs,
...action.payload.pendingConfigs,
...action.payload.disabledConfigs,
});
state.disabledConfigs = state.allConfigs.filter((monitor) => !monitor.isEnabled);
state.loaded = true;

View file

@ -438,7 +438,7 @@ function getMonitorDetailsMockSlice() {
tags: [],
timeout: null,
name: 'One pixel monitor',
locations: [{ isServiceManaged: true, id: 'us_central' }],
locations: [{ isServiceManaged: true, id: 'us_central', label: 'US Central' }],
namespace: 'default',
origin: SourceType.UI,
max_attempts: 2,

View file

@ -153,6 +153,23 @@ export async function queryMonitorStatus({
const downConfigs: Record<string, OverviewStatusMetaData> = {};
const monitorsWithoutData = new Map(Object.entries(cloneDeep(monitorLocationsMap)));
const pendingConfigs: Record<string, OverviewStatusMetaData> = {};
const disabledConfigs: Record<string, OverviewStatusMetaData> = {};
monitors
.filter((monitor) => !monitor.attributes[ConfigKey.ENABLED])
.forEach((monitor) => {
const monitorQueryId = monitor.attributes[ConfigKey.MONITOR_QUERY_ID];
monitor.attributes[ConfigKey.LOCATIONS]?.forEach((location) => {
disabledConfigs[`${monitorQueryIdToConfigIdMap[monitorQueryId]}-${location.id}`] = {
configId: `${monitorQueryIdToConfigIdMap[monitorQueryId]}`,
monitorQueryId,
status: 'disabled',
locationId: location.id,
locationLabel: location.label,
...getMonitorMeta(monitor),
};
});
});
const queries: MsearchMultisearchBody[] = times(pageCount).map((i) => {
const idsToQuery = (monitorQueryIds as string[]).slice(i * idSize, i * idSize + idSize);
@ -164,78 +181,73 @@ export async function queryMonitorStatus({
}).body;
});
const { responses } = await esClient.msearch<StatusQueryParams, OverviewPing>(
queries,
'getCurrentStatusOverview'
);
if (queries.length) {
const { responses } = await esClient.msearch<StatusQueryParams, OverviewPing>(
queries,
'getCurrentStatusOverview'
);
responses.forEach((result) => {
result.aggregations?.id.buckets.forEach(({ location, key: queryId }) => {
const locationSummaries = location.buckets.map(({ status, key: locationName }) => {
const ping = status.hits.hits[0]._source;
return { location: locationName, ping };
});
responses.forEach((result) => {
result.aggregations?.id.buckets.forEach(({ location, key: queryId }) => {
const locationSummaries = location.buckets.map(({ status, key: locationName }) => {
const ping = status.hits.hits[0]._source;
return { location: locationName, ping };
});
const monitor = monitors.find((m) => m.attributes[ConfigKey.MONITOR_QUERY_ID] === queryId)!;
const monitor = monitors.find((m) => m.attributes[ConfigKey.MONITOR_QUERY_ID] === queryId)!;
// discard any locations that are not in the monitorLocationsMap for the given monitor as well as those which are
// in monitorLocationsMap but not in listOfLocations
const monLocations = monitorLocationsMap?.[queryId];
const monQueriedLocations = intersection(monLocations, monitorLocationIds);
monQueriedLocations?.forEach((monLocation) => {
const locationSummary = locationSummaries.find(
(summary) => summary.location === monLocation
);
if (locationSummary) {
const { ping } = locationSummary;
const downCount = ping.summary?.down ?? 0;
const upCount = ping.summary?.up ?? 0;
const configId = ping.config_id;
const monitorQueryId = ping.monitor.id;
const meta = {
ping,
configId,
monitorQueryId,
locationId: monLocation,
timestamp: ping['@timestamp'],
locationLabel: ping.observer.geo!.name!,
name: monitor.attributes[ConfigKey.NAME],
schedule: monitor.attributes[ConfigKey.SCHEDULE].number,
tags: monitor.attributes[ConfigKey.TAGS],
isEnabled: monitor.attributes[ConfigKey.ENABLED],
type: monitor.attributes[ConfigKey.MONITOR_TYPE],
projectId: monitor.attributes[ConfigKey.PROJECT_ID],
isStatusAlertEnabled: isStatusEnabled(monitor.attributes[ConfigKey.ALERT_CONFIG]),
updated_at: monitor.updated_at,
};
if (downCount > 0) {
down += 1;
downConfigs[`${configId}-${monLocation}`] = {
...meta,
status: 'down',
};
} else if (upCount > 0) {
up += 1;
upConfigs[`${configId}-${monLocation}`] = {
...meta,
status: 'up',
};
}
const monitorsMissingData = monitorsWithoutData.get(monitorQueryId) || [];
monitorsWithoutData.set(
monitorQueryId,
monitorsMissingData?.filter((loc) => loc !== monLocation)
// discard any locations that are not in the monitorLocationsMap for the given monitor as well as those which are
// in monitorLocationsMap but not in listOfLocations
const monLocations = monitorLocationsMap?.[queryId];
const monQueriedLocations = intersection(monLocations, monitorLocationIds);
monQueriedLocations?.forEach((monLocation) => {
const locationSummary = locationSummaries.find(
(summary) => summary.location === monLocation
);
if (!monitorsWithoutData.get(monitorQueryId)?.length) {
monitorsWithoutData.delete(monitorQueryId);
if (locationSummary) {
const { ping } = locationSummary;
const downCount = ping.summary?.down ?? 0;
const upCount = ping.summary?.up ?? 0;
const configId = ping.config_id;
const monitorQueryId = ping.monitor.id;
const meta = {
ping,
configId,
monitorQueryId,
locationId: monLocation,
timestamp: ping['@timestamp'],
locationLabel: ping.observer.geo!.name!,
...getMonitorMeta(monitor),
};
if (downCount > 0) {
down += 1;
downConfigs[`${configId}-${monLocation}`] = {
...meta,
status: 'down',
};
} else if (upCount > 0) {
up += 1;
upConfigs[`${configId}-${monLocation}`] = {
...meta,
status: 'up',
};
}
const monitorsMissingData = monitorsWithoutData.get(monitorQueryId) || [];
monitorsWithoutData.set(
monitorQueryId,
monitorsMissingData?.filter((loc) => loc !== monLocation)
);
if (!monitorsWithoutData.get(monitorQueryId)?.length) {
monitorsWithoutData.delete(monitorQueryId);
}
}
}
});
});
});
});
}
// identify the remaining monitors without data, to determine pending monitors
for (const [queryId, locs] of monitorsWithoutData) {
@ -269,5 +281,19 @@ export async function queryMonitorStatus({
downConfigs,
pendingConfigs,
enabledMonitorQueryIds: monitorQueryIds,
disabledConfigs,
};
}
const getMonitorMeta = (monitor: SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>) => {
return {
name: monitor.attributes[ConfigKey.NAME],
schedule: monitor.attributes[ConfigKey.SCHEDULE].number,
tags: monitor.attributes[ConfigKey.TAGS],
isEnabled: monitor.attributes[ConfigKey.ENABLED],
type: monitor.attributes[ConfigKey.MONITOR_TYPE],
projectId: monitor.attributes[ConfigKey.PROJECT_ID],
isStatusAlertEnabled: isStatusEnabled(monitor.attributes[ConfigKey.ALERT_CONFIG]),
updated_at: monitor.updated_at,
};
};

View file

@ -262,6 +262,7 @@ describe('current status route', () => {
})
).toMatchInlineSnapshot(`
Object {
"disabledConfigs": Object {},
"down": 1,
"downConfigs": Object {
"id2-Europe - Germany": Object {
@ -527,6 +528,7 @@ describe('current status route', () => {
})
).toMatchInlineSnapshot(`
Object {
"disabledConfigs": Object {},
"down": 1,
"downConfigs": Object {
"id2-Europe - Germany": Object {
@ -820,6 +822,7 @@ describe('current status route', () => {
})
).toMatchInlineSnapshot(`
Object {
"disabledConfigs": Object {},
"down": 1,
"downConfigs": Object {
"id2-Europe - Germany": Object {

View file

@ -67,6 +67,7 @@ export async function getStatus(context: RouteContext, params: OverviewStatusQue
ConfigKey.NAME,
ConfigKey.TAGS,
ConfigKey.PROJECT_ID,
ConfigKey.ALERT_CONFIG,
],
});
@ -94,15 +95,16 @@ export async function getStatus(context: RouteContext, params: OverviewStatusQue
to: 'now',
};
const { up, down, pending, upConfigs, downConfigs, pendingConfigs } = await queryMonitorStatus({
range,
monitors: allMonitors,
monitorLocationsMap,
monitorQueryIdToConfigIdMap,
esClient: syntheticsEsClient,
monitorLocationIds: listOfLocationAfterFilter,
monitorQueryIds: enabledMonitorQueryIds,
});
const { up, down, pending, upConfigs, downConfigs, pendingConfigs, disabledConfigs } =
await queryMonitorStatus({
range,
monitors: allMonitors,
monitorLocationsMap,
monitorQueryIdToConfigIdMap,
esClient: syntheticsEsClient,
monitorLocationIds: listOfLocationAfterFilter,
monitorQueryIds: enabledMonitorQueryIds,
});
return {
allIds,
@ -118,6 +120,7 @@ export async function getStatus(context: RouteContext, params: OverviewStatusQue
upConfigs,
downConfigs,
pendingConfigs,
disabledConfigs,
};
}

View file

@ -28,7 +28,7 @@ const monitor850UI = {
tags: [],
timeout: '16',
name: 'Dominique Clarke',
locations: [{ id: 'us_central', isServiceManaged: true }],
locations: [{ id: 'us_central', isServiceManaged: true }] as any,
namespace: 'default',
origin: 'ui',
journey_id: '',

View file

@ -27,7 +27,7 @@ export const httpUI = {
tags: [],
timeout: '16',
name: 'Test monitor',
locations: [{ id: 'us_central', isServiceManaged: true }],
locations: [{ id: 'us_central', isServiceManaged: true }] as any,
namespace: 'default',
origin: 'ui',
journey_id: '',

View file

@ -288,7 +288,7 @@ const dummyBrowserConfig: Partial<MonitorFields> & {
tags: [],
timeout: null,
name: 'Browser monitor',
locations: [{ isServiceManaged: false, id: '1' }],
locations: [{ isServiceManaged: false, id: '1', label: 'Fleet managed' }],
namespace: 'default',
origin: SourceType.UI,
journey_id: '',