diff --git a/public/app/features/annotations/annotations_srv.ts b/public/app/features/annotations/annotations_srv.ts index 2863ecdd843..c798f33d3a9 100644 --- a/public/app/features/annotations/annotations_srv.ts +++ b/public/app/features/annotations/annotations_srv.ts @@ -3,6 +3,7 @@ import './editor_ctrl'; import angular from 'angular'; import _ from 'lodash'; import coreModule from 'app/core/core_module'; +import {makeRegions, dedupAnnotations} from './events_processing'; export class AnnotationsSrv { globalAnnotationsPromise: any; @@ -161,83 +162,4 @@ export class AnnotationsSrv { } } -/** - * This function converts annotation events into set - * of single events and regions (event consist of two) - * @param annotations - * @param options - */ -function makeRegions(annotations, options) { - let [regionEvents, singleEvents] = _.partition(annotations, 'regionId'); - let regions = getRegions(regionEvents, options.range); - annotations = _.concat(regions, singleEvents); - return annotations; -} - -function getRegions(events, range) { - let region_events = _.filter(events, event => { - return event.regionId; - }); - let regions = _.groupBy(region_events, 'regionId'); - regions = _.compact( - _.map(regions, region_events => { - let region_obj = _.head(region_events); - if (region_events && region_events.length > 1) { - region_obj.timeEnd = region_events[1].time; - region_obj.isRegion = true; - return region_obj; - } else { - if (region_events && region_events.length) { - // Don't change proper region object - if (!region_obj.time || !region_obj.timeEnd) { - // This is cut region - if (isStartOfRegion(region_obj)) { - region_obj.timeEnd = range.to.valueOf() - 1; - } else { - // Start time = null - region_obj.timeEnd = region_obj.time; - region_obj.time = range.from.valueOf() + 1; - } - region_obj.isRegion = true; - } - - return region_obj; - } - } - }), - ); - - return regions; -} - -function isStartOfRegion(event): boolean { - return event.id && event.id === event.regionId; -} - -function dedupAnnotations(annotations) { - let dedup = []; - - // Split events by annotationId property existance - let events = _.partition(annotations, 'id'); - - let eventsById = _.groupBy(events[0], 'id'); - dedup = _.map(eventsById, eventGroup => { - if (eventGroup.length > 1 && !_.every(eventGroup, isPanelAlert)) { - // Get first non-panel alert - return _.find(eventGroup, event => { - return event.eventType !== 'panel-alert'; - }); - } else { - return _.head(eventGroup); - } - }); - - dedup = _.concat(dedup, events[1]); - return dedup; -} - -function isPanelAlert(event) { - return event.eventType === 'panel-alert'; -} - coreModule.service('annotationsSrv', AnnotationsSrv); diff --git a/public/app/features/annotations/events_processing.ts b/public/app/features/annotations/events_processing.ts new file mode 100644 index 00000000000..27f74d89b5b --- /dev/null +++ b/public/app/features/annotations/events_processing.ts @@ -0,0 +1,80 @@ +import _ from 'lodash'; + +/** + * This function converts annotation events into set + * of single events and regions (event consist of two) + * @param annotations + * @param options + */ +export function makeRegions(annotations, options) { + let [regionEvents, singleEvents] = _.partition(annotations, 'regionId'); + let regions = getRegions(regionEvents, options.range); + annotations = _.concat(regions, singleEvents); + return annotations; +} + +function getRegions(events, range) { + let region_events = _.filter(events, event => { + return event.regionId; + }); + let regions = _.groupBy(region_events, 'regionId'); + regions = _.compact( + _.map(regions, region_events => { + let region_obj = _.head(region_events); + if (region_events && region_events.length > 1) { + region_obj.timeEnd = region_events[1].time; + region_obj.isRegion = true; + return region_obj; + } else { + if (region_events && region_events.length) { + // Don't change proper region object + if (!region_obj.time || !region_obj.timeEnd) { + // This is cut region + if (isStartOfRegion(region_obj)) { + region_obj.timeEnd = range.to.valueOf() - 1; + } else { + // Start time = null + region_obj.timeEnd = region_obj.time; + region_obj.time = range.from.valueOf() + 1; + } + region_obj.isRegion = true; + } + + return region_obj; + } + } + }), + ); + + return regions; +} + +function isStartOfRegion(event): boolean { + return event.id && event.id === event.regionId; +} + +export function dedupAnnotations(annotations) { + let dedup = []; + + // Split events by annotationId property existance + let events = _.partition(annotations, 'id'); + + let eventsById = _.groupBy(events[0], 'id'); + dedup = _.map(eventsById, eventGroup => { + if (eventGroup.length > 1 && !_.every(eventGroup, isPanelAlert)) { + // Get first non-panel alert + return _.find(eventGroup, event => { + return event.eventType !== 'panel-alert'; + }); + } else { + return _.head(eventGroup); + } + }); + + dedup = _.concat(dedup, events[1]); + return dedup; +} + +function isPanelAlert(event) { + return event.eventType === 'panel-alert'; +} diff --git a/public/app/features/annotations/specs/annotations_srv_specs.jest.ts b/public/app/features/annotations/specs/annotations_srv_specs.jest.ts new file mode 100644 index 00000000000..823bb8996db --- /dev/null +++ b/public/app/features/annotations/specs/annotations_srv_specs.jest.ts @@ -0,0 +1,83 @@ +import {makeRegions, dedupAnnotations} from '../events_processing'; + +describe('Annotations', () => { + + describe('Annotations regions', () => { + let testAnnotations: any[]; + + beforeEach(() => { + testAnnotations = [ + {id: 1, time: 1}, + {id: 2, time: 2}, + {id: 3, time: 3, regionId: 3}, + {id: 4, time: 5, regionId: 3}, + {id: 5, time: 4, regionId: 5}, + {id: 6, time: 8, regionId: 5} + ]; + }); + + it('should convert single region events to regions', () => { + const range = {from: 0, to: 10}; + const expectedAnnotations = [ + {id: 3, regionId: 3, isRegion: true, time: 3, timeEnd: 5}, + {id: 5, regionId: 5, isRegion: true, time: 4, timeEnd: 8}, + {id: 1, time: 1}, + {id: 2, time: 2} + ]; + + let regions = makeRegions(testAnnotations, {range: range}); + expect(regions).toEqual(expectedAnnotations); + }); + + it('should cut regions to current time range', () => { + const range = {from: 0, to: 8}; + testAnnotations = [ + {id: 5, time: 4, regionId: 5} + ]; + const expectedAnnotations = [ + {id: 5, regionId: 5, isRegion: true, time: 4, timeEnd: 7} + ]; + + let regions = makeRegions(testAnnotations, {range: range}); + expect(regions).toEqual(expectedAnnotations); + }); + }); + + describe('Annotations deduplication', () => { + it('should remove duplicated annotations', () => { + const testAnnotations = [ + {id: 1, time: 1}, + {id: 2, time: 2}, + {id: 2, time: 2}, + {id: 5, time: 5}, + {id: 5, time: 5} + ]; + const expectedAnnotations = [ + {id: 1, time: 1}, + {id: 2, time: 2}, + {id: 5, time: 5} + ]; + + let deduplicated = dedupAnnotations(testAnnotations); + expect(deduplicated).toEqual(expectedAnnotations); + }); + + it('should leave non "panel-alert" event if present', () => { + const testAnnotations = [ + {id: 1, time: 1}, + {id: 2, time: 2}, + {id: 2, time: 2, eventType: 'panel-alert'}, + {id: 5, time: 5}, + {id: 5, time: 5} + ]; + const expectedAnnotations = [ + {id: 1, time: 1}, + {id: 2, time: 2}, + {id: 5, time: 5} + ]; + + let deduplicated = dedupAnnotations(testAnnotations); + expect(deduplicated).toEqual(expectedAnnotations); + }); + }); +});