diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx index f138c08c0f993..41fd190b0e85d 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx @@ -277,7 +277,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeP } fullWidth onChange={(e) => setAlertParams('trackingEvent', e.target.value)} - options={[conditionOptions[0]]} // TODO: Make all options avab. before merge + options={conditionOptions} /> </div> </EuiFormRow> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts index 0358fcd66a467..746d73bf28627 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts @@ -7,6 +7,7 @@ export enum TrackingEvent { entered = 'entered', exited = 'exited', + crossed = 'crossed', } export interface GeoThresholdAlertParams { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts index e223cdb7ea545..6efa68a2e5a1e 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts @@ -144,11 +144,14 @@ export function getMovedEntities( [] ) // Do not track entries to or exits from 'other' - .filter((entityMovementDescriptor: EntityMovementDescriptor) => - trackingEvent === 'entered' - ? entityMovementDescriptor.currLocation.shapeId !== OTHER_CATEGORY - : entityMovementDescriptor.prevLocation.shapeId !== OTHER_CATEGORY - ) + .filter((entityMovementDescriptor: EntityMovementDescriptor) => { + if (trackingEvent !== 'crossed') { + return trackingEvent === 'entered' + ? entityMovementDescriptor.currLocation.shapeId !== OTHER_CATEGORY + : entityMovementDescriptor.prevLocation.shapeId !== OTHER_CATEGORY; + } + return true; + }) ); } @@ -253,27 +256,36 @@ export const getGeoThresholdExecutor = (log: Logger) => movedEntities.forEach(({ entityName, currLocation, prevLocation }) => { const toBoundaryName = shapesIdsNamesMap[currLocation.shapeId] || currLocation.shapeId; const fromBoundaryName = shapesIdsNamesMap[prevLocation.shapeId] || prevLocation.shapeId; - services - .alertInstanceFactory(`${entityName}-${toBoundaryName || currLocation.shapeId}`) - .scheduleActions(ActionGroupId, { - entityId: entityName, - timeOfDetection: new Date(currIntervalEndTime).getTime(), - crossingLine: `LINESTRING (${prevLocation.location[0]} ${prevLocation.location[1]}, ${currLocation.location[0]} ${currLocation.location[1]})`, + let alertInstance; + if (params.trackingEvent === 'entered') { + alertInstance = `${entityName}-${toBoundaryName || currLocation.shapeId}`; + } else if (params.trackingEvent === 'exited') { + alertInstance = `${entityName}-${fromBoundaryName || prevLocation.shapeId}`; + } else { + // == 'crossed' + alertInstance = `${entityName}-${fromBoundaryName || prevLocation.shapeId}-${ + toBoundaryName || currLocation.shapeId + }`; + } + services.alertInstanceFactory(alertInstance).scheduleActions(ActionGroupId, { + entityId: entityName, + timeOfDetection: new Date(currIntervalEndTime).getTime(), + crossingLine: `LINESTRING (${prevLocation.location[0]} ${prevLocation.location[1]}, ${currLocation.location[0]} ${currLocation.location[1]})`, - toEntityLocation: `POINT (${currLocation.location[0]} ${currLocation.location[1]})`, - toEntityDateTime: currLocation.date, - toEntityDocumentId: currLocation.docId, + toEntityLocation: `POINT (${currLocation.location[0]} ${currLocation.location[1]})`, + toEntityDateTime: currLocation.date, + toEntityDocumentId: currLocation.docId, - toBoundaryId: currLocation.shapeId, - toBoundaryName, + toBoundaryId: currLocation.shapeId, + toBoundaryName, - fromEntityLocation: `POINT (${prevLocation.location[0]} ${prevLocation.location[1]})`, - fromEntityDateTime: prevLocation.date, - fromEntityDocumentId: prevLocation.docId, + fromEntityLocation: `POINT (${prevLocation.location[0]} ${prevLocation.location[1]})`, + fromEntityDateTime: prevLocation.date, + fromEntityDocumentId: prevLocation.docId, - fromBoundaryId: prevLocation.shapeId, - fromBoundaryName, - }); + fromBoundaryId: prevLocation.shapeId, + fromBoundaryName, + }); }); // Combine previous results w/ current results for state of next run diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/geo_threshold.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/geo_threshold.test.ts index e4cee9c677713..5b5197ac62a39 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/geo_threshold.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/geo_threshold.test.ts @@ -99,7 +99,6 @@ describe('geo_threshold', () => { }); describe('getMovedEntities', () => { - const trackingEvent = 'entered'; it('should return empty array if only movements were within same shapes', async () => { const currLocationArr = [ { @@ -133,7 +132,7 @@ describe('geo_threshold', () => { shapeLocationId: 'sameShape2', }, ]; - const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, trackingEvent); + const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, 'entered'); expect(movedEntities).toEqual([]); }); @@ -170,7 +169,7 @@ describe('geo_threshold', () => { shapeLocationId: 'thisOneDidntMove', }, ]; - const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, trackingEvent); + const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, 'entered'); expect(movedEntities.length).toEqual(1); }); @@ -193,7 +192,7 @@ describe('geo_threshold', () => { shapeLocationId: 'oldShapeLocation', }, ]; - const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, trackingEvent); + const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, 'entered'); expect(movedEntities).toEqual([]); }); @@ -219,5 +218,51 @@ describe('geo_threshold', () => { const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, 'exited'); expect(movedEntities).toEqual([]); }); + + it('should not ignore "crossed" results from "other"', async () => { + const currLocationArr = [ + { + dateInShape: '2020-09-28T18:01:41.190Z', + docId: 'N-ng1XQB6yyY-xQxnGSM', + entityName: '936', + location: [-82.8814151789993, 41.62806099653244], + shapeLocationId: 'newShapeLocation', + }, + ]; + const prevLocationArr = [ + { + dateInShape: '2020-08-28T18:01:41.190Z', + docId: 'N-ng1XQB6yyY-xQxnGSM', + entityName: '936', + location: [-82.8814151789993, 40.62806099653244], + shapeLocationId: OTHER_CATEGORY, + }, + ]; + const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, 'crossed'); + expect(movedEntities.length).toEqual(1); + }); + + it('should not ignore "crossed" results to "other"', async () => { + const currLocationArr = [ + { + dateInShape: '2020-08-28T18:01:41.190Z', + docId: 'N-ng1XQB6yyY-xQxnGSM', + entityName: '936', + location: [-82.8814151789993, 40.62806099653244], + shapeLocationId: OTHER_CATEGORY, + }, + ]; + const prevLocationArr = [ + { + dateInShape: '2020-09-28T18:01:41.190Z', + docId: 'N-ng1XQB6yyY-xQxnGSM', + entityName: '936', + location: [-82.8814151789993, 41.62806099653244], + shapeLocationId: 'newShapeLocation', + }, + ]; + const movedEntities = getMovedEntities(currLocationArr, prevLocationArr, 'crossed'); + expect(movedEntities.length).toEqual(1); + }); }); });