mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Geo containment alert sparsity handling: preserve active status for non-updated alerts (#85364)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
3177f47451
commit
ad922d0f59
2 changed files with 165 additions and 19 deletions
|
@ -11,7 +11,7 @@ import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_qu
|
|||
import { AlertServices, AlertTypeState } from '../../../../alerts/server';
|
||||
import { ActionGroupId, GEO_CONTAINMENT_ID, GeoContainmentParams } from './alert_type';
|
||||
|
||||
interface LatestEntityLocation {
|
||||
export interface LatestEntityLocation {
|
||||
location: number[];
|
||||
shapeLocationId: string;
|
||||
dateInShape: string | null;
|
||||
|
@ -94,6 +94,40 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date {
|
|||
return adjustedDate;
|
||||
}
|
||||
|
||||
export function getActiveEntriesAndGenerateAlerts(
|
||||
prevLocationMap: Record<string, LatestEntityLocation>,
|
||||
currLocationMap: Map<string, LatestEntityLocation>,
|
||||
alertInstanceFactory: (
|
||||
x: string
|
||||
) => { scheduleActions: (x: string, y: Record<string, unknown>) => void },
|
||||
shapesIdsNamesMap: Record<string, unknown>,
|
||||
currIntervalEndTime: Date
|
||||
) {
|
||||
const allActiveEntriesMap: Map<string, LatestEntityLocation> = new Map([
|
||||
...Object.entries(prevLocationMap || {}),
|
||||
...currLocationMap,
|
||||
]);
|
||||
allActiveEntriesMap.forEach(({ location, shapeLocationId, dateInShape, docId }, entityName) => {
|
||||
const containingBoundaryName = shapesIdsNamesMap[shapeLocationId] || shapeLocationId;
|
||||
const context = {
|
||||
entityId: entityName,
|
||||
entityDateTime: dateInShape ? new Date(dateInShape).toISOString() : null,
|
||||
entityDocumentId: docId,
|
||||
detectionDateTime: new Date(currIntervalEndTime).toISOString(),
|
||||
entityLocation: `POINT (${location[0]} ${location[1]})`,
|
||||
containingBoundaryId: shapeLocationId,
|
||||
containingBoundaryName,
|
||||
};
|
||||
const alertInstanceId = `${entityName}-${containingBoundaryName}`;
|
||||
if (shapeLocationId === OTHER_CATEGORY) {
|
||||
allActiveEntriesMap.delete(entityName);
|
||||
} else {
|
||||
alertInstanceFactory(alertInstanceId).scheduleActions(ActionGroupId, context);
|
||||
}
|
||||
});
|
||||
return allActiveEntriesMap;
|
||||
}
|
||||
|
||||
export const getGeoContainmentExecutor = (log: Logger) =>
|
||||
async function ({
|
||||
previousStartedAt,
|
||||
|
@ -153,26 +187,17 @@ export const getGeoContainmentExecutor = (log: Logger) =>
|
|||
params.geoField
|
||||
);
|
||||
|
||||
// Cycle through new alert statuses and set active
|
||||
currLocationMap.forEach(({ location, shapeLocationId, dateInShape, docId }, entityName) => {
|
||||
const containingBoundaryName = shapesIdsNamesMap[shapeLocationId] || shapeLocationId;
|
||||
const context = {
|
||||
entityId: entityName,
|
||||
entityDateTime: new Date(currIntervalEndTime).toISOString(),
|
||||
entityDocumentId: docId,
|
||||
detectionDateTime: new Date(currIntervalEndTime).toISOString(),
|
||||
entityLocation: `POINT (${location[0]} ${location[1]})`,
|
||||
containingBoundaryId: shapeLocationId,
|
||||
containingBoundaryName,
|
||||
};
|
||||
const alertInstanceId = `${entityName}-${containingBoundaryName}`;
|
||||
if (shapeLocationId !== OTHER_CATEGORY) {
|
||||
services.alertInstanceFactory(alertInstanceId).scheduleActions(ActionGroupId, context);
|
||||
}
|
||||
});
|
||||
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
|
||||
state.prevLocationMap as Record<string, LatestEntityLocation>,
|
||||
currLocationMap,
|
||||
services.alertInstanceFactory,
|
||||
shapesIdsNamesMap,
|
||||
currIntervalEndTime
|
||||
);
|
||||
|
||||
return {
|
||||
shapesFilters,
|
||||
shapesIdsNamesMap,
|
||||
prevLocationMap: Object.fromEntries(allActiveEntriesMap),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
import sampleJsonResponse from './es_sample_response.json';
|
||||
import sampleJsonResponseWithNesting from './es_sample_response_with_nesting.json';
|
||||
import { transformResults } from '../geo_containment';
|
||||
import { getActiveEntriesAndGenerateAlerts, transformResults } from '../geo_containment';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { OTHER_CATEGORY } from '../es_query_builder';
|
||||
|
||||
describe('geo_containment', () => {
|
||||
describe('transformResults', () => {
|
||||
|
@ -116,4 +117,124 @@ describe('geo_containment', () => {
|
|||
expect(transformedResults).toEqual(new Map());
|
||||
});
|
||||
});
|
||||
|
||||
describe('getActiveEntriesAndGenerateAlerts', () => {
|
||||
const testAlertActionArr: unknown[] = [];
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
testAlertActionArr.length = 0;
|
||||
});
|
||||
|
||||
const currLocationMap = new Map([
|
||||
[
|
||||
'a',
|
||||
{
|
||||
location: [0, 0],
|
||||
shapeLocationId: '123',
|
||||
dateInShape: 'Wed Dec 09 2020 14:31:31 GMT-0700 (Mountain Standard Time)',
|
||||
docId: 'docId1',
|
||||
},
|
||||
],
|
||||
[
|
||||
'b',
|
||||
{
|
||||
location: [0, 0],
|
||||
shapeLocationId: '456',
|
||||
dateInShape: 'Wed Dec 09 2020 15:31:31 GMT-0700 (Mountain Standard Time)',
|
||||
docId: 'docId2',
|
||||
},
|
||||
],
|
||||
[
|
||||
'c',
|
||||
{
|
||||
location: [0, 0],
|
||||
shapeLocationId: '789',
|
||||
dateInShape: 'Wed Dec 09 2020 16:31:31 GMT-0700 (Mountain Standard Time)',
|
||||
docId: 'docId3',
|
||||
},
|
||||
],
|
||||
]);
|
||||
const emptyShapesIdsNamesMap = {};
|
||||
|
||||
const scheduleActions = jest.fn((alertInstance: string, context: Record<string, unknown>) => {
|
||||
testAlertActionArr.push(context.entityId);
|
||||
});
|
||||
const alertInstanceFactory = (x: string) => ({ scheduleActions });
|
||||
const currentDateTime = new Date();
|
||||
|
||||
it('should use currently active entities if no older entity entries', () => {
|
||||
const emptyPrevLocationMap = {};
|
||||
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
|
||||
emptyPrevLocationMap,
|
||||
currLocationMap,
|
||||
alertInstanceFactory,
|
||||
emptyShapesIdsNamesMap,
|
||||
currentDateTime
|
||||
);
|
||||
expect(allActiveEntriesMap).toEqual(currLocationMap);
|
||||
expect(scheduleActions.mock.calls.length).toEqual(allActiveEntriesMap.size);
|
||||
expect(testAlertActionArr).toEqual([...allActiveEntriesMap.keys()]);
|
||||
});
|
||||
it('should overwrite older identical entity entries', () => {
|
||||
const prevLocationMapWithIdenticalEntityEntry = {
|
||||
a: {
|
||||
location: [0, 0],
|
||||
shapeLocationId: '999',
|
||||
dateInShape: 'Wed Dec 09 2020 12:31:31 GMT-0700 (Mountain Standard Time)',
|
||||
docId: 'docId7',
|
||||
},
|
||||
};
|
||||
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
|
||||
prevLocationMapWithIdenticalEntityEntry,
|
||||
currLocationMap,
|
||||
alertInstanceFactory,
|
||||
emptyShapesIdsNamesMap,
|
||||
currentDateTime
|
||||
);
|
||||
expect(allActiveEntriesMap).toEqual(currLocationMap);
|
||||
expect(scheduleActions.mock.calls.length).toEqual(allActiveEntriesMap.size);
|
||||
expect(testAlertActionArr).toEqual([...allActiveEntriesMap.keys()]);
|
||||
});
|
||||
it('should preserve older non-identical entity entries', () => {
|
||||
const prevLocationMapWithNonIdenticalEntityEntry = {
|
||||
d: {
|
||||
location: [0, 0],
|
||||
shapeLocationId: '999',
|
||||
dateInShape: 'Wed Dec 09 2020 12:31:31 GMT-0700 (Mountain Standard Time)',
|
||||
docId: 'docId7',
|
||||
},
|
||||
};
|
||||
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
|
||||
prevLocationMapWithNonIdenticalEntityEntry,
|
||||
currLocationMap,
|
||||
alertInstanceFactory,
|
||||
emptyShapesIdsNamesMap,
|
||||
currentDateTime
|
||||
);
|
||||
expect(allActiveEntriesMap).not.toEqual(currLocationMap);
|
||||
expect(allActiveEntriesMap.has('d')).toBeTruthy();
|
||||
expect(scheduleActions.mock.calls.length).toEqual(allActiveEntriesMap.size);
|
||||
expect(testAlertActionArr).toEqual([...allActiveEntriesMap.keys()]);
|
||||
});
|
||||
it('should remove "other" entries and schedule the expected number of actions', () => {
|
||||
const emptyPrevLocationMap = {};
|
||||
const currLocationMapWithOther = new Map(currLocationMap).set('d', {
|
||||
location: [0, 0],
|
||||
shapeLocationId: OTHER_CATEGORY,
|
||||
dateInShape: 'Wed Dec 09 2020 14:31:31 GMT-0700 (Mountain Standard Time)',
|
||||
docId: 'docId1',
|
||||
});
|
||||
expect(currLocationMapWithOther).not.toEqual(currLocationMap);
|
||||
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
|
||||
emptyPrevLocationMap,
|
||||
currLocationMapWithOther,
|
||||
alertInstanceFactory,
|
||||
emptyShapesIdsNamesMap,
|
||||
currentDateTime
|
||||
);
|
||||
expect(allActiveEntriesMap).toEqual(currLocationMap);
|
||||
expect(scheduleActions.mock.calls.length).toEqual(allActiveEntriesMap.size);
|
||||
expect(testAlertActionArr).toEqual([...allActiveEntriesMap.keys()]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue