]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Notification queue 25325/head
authorStephan Müller <smueller@suse.com>
Mon, 26 Nov 2018 14:59:26 +0000 (15:59 +0100)
committerStephan Müller <smueller@suse.com>
Wed, 12 Dec 2018 13:53:09 +0000 (14:53 +0100)
It's now possible to send an array of notification configs to the notification
service. These array is cached for, by default 500ms, before this array
is unified and than converted to full notifications that are seen by the
user.

Useful if different service can come to the same conclusions regarding
the notification. This is the case for the prometheus notification service
and the prometheus alert service.

Fixes: https://tracker.ceph.com/issues/37469
Signed-off-by: Stephan Müller <smueller@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.ts

index a229c982ebed11dc23c42d7f19f7cdfebf379001..b0b88df273687b808bda8f8e8d6ce3b1606a2978 100644 (file)
@@ -123,4 +123,44 @@ describe('NotificationService', () => {
     expect(notification.title).toBe(`Failed to create RBD 'somePool/someImage'`);
     expect(notification.message).toBe(`Name is already used by RBD 'somePool/someImage'.`);
   }));
+
+  describe('notification queue', () => {
+    const n1 = new CdNotificationConfig(NotificationType.success, 'Some success');
+    const n2 = new CdNotificationConfig(NotificationType.info, 'Some info');
+
+    beforeEach(() => {
+      spyOn(notificationService, 'show').and.stub();
+    });
+
+    it('filters out duplicated notifications on single call', fakeAsync(() => {
+      notificationService.queueNotifications([n1, n1, n2, n2]);
+      tick(500);
+      expect(notificationService.show).toHaveBeenCalledTimes(2);
+    }));
+
+    it('filters out duplicated notifications presented in different calls', fakeAsync(() => {
+      notificationService.queueNotifications([n1, n2]);
+      notificationService.queueNotifications([n1, n2]);
+      tick(500);
+      expect(notificationService.show).toHaveBeenCalledTimes(2);
+    }));
+
+    it('will reset the timeout on every call', fakeAsync(() => {
+      notificationService.queueNotifications([n1, n2]);
+      tick(400);
+      notificationService.queueNotifications([n1, n2]);
+      tick(100);
+      expect(notificationService.show).toHaveBeenCalledTimes(0);
+      tick(400);
+      expect(notificationService.show).toHaveBeenCalledTimes(2);
+    }));
+
+    it('wont filter out duplicated notifications if timeout was reached before', fakeAsync(() => {
+      notificationService.queueNotifications([n1, n2]);
+      tick(500);
+      notificationService.queueNotifications([n1, n2]);
+      tick(500);
+      expect(notificationService.show).toHaveBeenCalledTimes(4);
+    }));
+  });
 });
index 8874e27282644ce13673fd6ec7e0fa32f8ca41de..de1175b8e54d45b4f058367c70f416dcbf4912f2 100644 (file)
@@ -16,10 +16,12 @@ import { TaskMessageService } from './task-message.service';
 export class NotificationService {
   // Observable sources
   private dataSource = new BehaviorSubject<CdNotification[]>([]);
+  private queuedNotifications: CdNotificationConfig[] = [];
 
   // Observable streams
   data$ = this.dataSource.asObservable();
 
+  private queueTimeoutId: number;
   KEY = 'cdNotifications';
 
   constructor(public toastr: ToastsManager, private taskMessageService: TaskMessageService) {
@@ -63,6 +65,21 @@ export class NotificationService {
     localStorage.setItem(this.KEY, JSON.stringify(recent));
   }
 
+  queueNotifications(notifications: CdNotificationConfig[]) {
+    this.queuedNotifications = this.queuedNotifications.concat(notifications);
+    this.cancel(this.queueTimeoutId);
+    this.queueTimeoutId = window.setTimeout(() => {
+      this.sendQueuedNotifications();
+    }, 500);
+  }
+
+  private sendQueuedNotifications() {
+    _.uniqWith(this.queuedNotifications, _.isEqual).forEach((notification) => {
+      this.show(notification);
+    });
+    this.queuedNotifications = [];
+  }
+
   /**
    * Method for showing a notification.
    * @param {NotificationType} type toastr type
@@ -86,7 +103,7 @@ export class NotificationService {
     } else {
       type = arg;
     }
-    return setTimeout(() => {
+    return window.setTimeout(() => {
       this.save(type, title, message);
       if (!message) {
         message = '';
@@ -127,6 +144,6 @@ export class NotificationService {
    * @param {number} timeoutId A number representing the ID of the timeout to be canceled.
    */
   cancel(timeoutId) {
-    clearTimeout(timeoutId);
+    window.clearTimeout(timeoutId);
   }
 }