[CORD-2277] Two stage delete for models

Change-Id: Ic1b1d59a9f1d6d963d10951e694cf963f41d84d5
(cherry picked from commit e9cdf9ab429f1899b88552d8b0fd177c7cde9c94)
diff --git a/src/app/datasources/helpers/store.helpers.spec.ts b/src/app/datasources/helpers/store.helpers.spec.ts
index 2f617bc..9c4cfc6 100644
--- a/src/app/datasources/helpers/store.helpers.spec.ts
+++ b/src/app/datasources/helpers/store.helpers.spec.ts
@@ -33,6 +33,7 @@
 let resource: ng.resource.IResourceClass<any>;
 let $resource: ng.resource.IResourceService;
 let xosModelCache: IXosModeldefsCache;
+let $log: ng.ILogService;
 
 describe('The StoreHelpers service', () => {
 
@@ -54,14 +55,17 @@
   });
 
   beforeEach(angular.mock.inject((
+
     StoreHelpers: IStoreHelpersService,
     XosModeldefsCache: IXosModeldefsCache,
-    _$resource_: ng.resource.IResourceService
+    _$resource_: ng.resource.IResourceService,
+    _$log_: ng.ILogService
   ) => {
     $resource = _$resource_;
     resource = $resource('/test');
     xosModelCache = XosModeldefsCache;
     service = StoreHelpers;
+    $log = _$log_;
   }));
 
   it('should have an update collection method', () => {
@@ -102,17 +106,35 @@
     });
   });
 
-  describe('when updating a collection', () => {
-
+  describe('when removing an item from a collection', () => {
     beforeEach(() => {
       subject = new BehaviorSubject([
         new resource({id: 1, name: 'test'})
       ]);
     });
 
+    describe('the updateCollection method', () => {
+      beforeEach(() => {
+        spyOn($log, 'error');
+      });
+
+      it('should log an error if called with a delete event', () => {
+        const event: IWSEvent = {
+          deleted: true,
+          model: 'Deleted',
+          msg: {
+            changed_fields: []
+          }
+        };
+        service.updateCollection(event, subject);
+        expect($log.error).toHaveBeenCalled();
+      });
+    });
+
     it('should remove a model if it has been deleted', () => {
       const event: IWSEvent = {
         model: 'Test',
+        deleted: true,
         msg: {
           object: {
             id: 1,
@@ -121,9 +143,35 @@
           changed_fields: ['deleted']
         }
       };
-      service.updateCollection(event, subject);
+      service.removeItemFromCollection(event, subject);
       expect(subject.value.length).toBe(0);
     });
+  });
+
+  describe('when updating a collection', () => {
+
+    beforeEach(() => {
+      subject = new BehaviorSubject([
+        new resource({id: 1, name: 'test'})
+      ]);
+    });
+
+    describe('the removeItemFromCollection method', () => {
+      beforeEach(() => {
+        spyOn($log, 'error');
+      });
+
+      it('should log an error if called with an update event', () => {
+        const event: IWSEvent = {
+          model: 'Deleted',
+          msg: {
+            changed_fields: []
+          }
+        };
+        service.removeItemFromCollection(event, subject);
+        expect($log.error).toHaveBeenCalled();
+      });
+    });
 
     it('should update a model if it has been updated', () => {
       const event: IWSEvent = {
diff --git a/src/app/datasources/helpers/store.helpers.ts b/src/app/datasources/helpers/store.helpers.ts
index 0df37c7..514b870 100644
--- a/src/app/datasources/helpers/store.helpers.ts
+++ b/src/app/datasources/helpers/store.helpers.ts
@@ -24,28 +24,34 @@
 
 export interface IStoreHelpersService {
   updateCollection(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any>;
+  removeItemFromCollection(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any>;
 }
 
 export class StoreHelpers implements IStoreHelpersService {
   static $inject = [
+    '$log',
     'ModelRest',
     'XosModeldefsCache'
   ];
 
   constructor (
+    private $log: ng.ILogService,
     private modelRest: IXosResourceService,
     private XosModeldefsCache: IXosModeldefsCache
   ) {
   }
 
   public updateCollection(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any> {
+    if (event.deleted) {
+      this.$log.error('[XosStoreHelpers] updateCollection method has been called for a delete event, in this cale please use "removeItemFromCollection"', event);
+      return;
+    }
     const collection: any[] = subject.value;
     const index: number = _.findIndex(collection, (i) => {
       // NOTE evaluate to use event.msg.pk
       return i.id === event.msg.object.id;
     });
     const exist: boolean = index > -1;
-    const isDeleted: boolean = _.includes(event.msg.changed_fields, 'deleted');
 
     // generate a resource for the model
     const modelDef = this.XosModeldefsCache.get(event.model); // get the model definition
@@ -53,16 +59,12 @@
     const resource = this.modelRest.getResource(endpoint);
     const model = new resource(event.msg.object);
 
-    // remove
-    if (exist && isDeleted) {
-       _.remove(collection, {id: event.msg.object.id});
-     }
     // Replace item at index using native splice
-    else if (exist && !isDeleted) {
+    if (exist) {
        collection.splice(index, 1, model);
      }
-    // if the element is not deleted add it
-    else if (!exist && !isDeleted) {
+    // if the element does not exist add it
+    else {
       collection.push(model);
      }
 
@@ -70,4 +72,15 @@
 
     return subject;
     }
+
+  public removeItemFromCollection(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any> {
+    if (!event.deleted) {
+      this.$log.error('[XosStoreHelpers] removeItemFromCollection method has been called for an update event, in this cale please use "updateCollection"', event);
+      return;
+    }
+    const collection: any[] = subject.value;
+    _.remove(collection, {id: event.msg.object.id});
+    subject.next(collection);
+    return subject;
+  }
 }
diff --git a/src/app/datasources/rest/model.rest.ts b/src/app/datasources/rest/model.rest.ts
index f390616..dcec5d4 100644
--- a/src/app/datasources/rest/model.rest.ts
+++ b/src/app/datasources/rest/model.rest.ts
@@ -64,6 +64,10 @@
       }
     };
 
+    // resource.prototype.$delete = function() {
+    //
+    // }
+
     return resource;
   }
 }
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index 10c4d2e..09bd715 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -73,7 +73,12 @@
       .filter((e: IWSEvent) => e.model === modelName)
       .subscribe(
         (event: IWSEvent) => {
-          this.storeHelpers.updateCollection(event, this._collections[modelName]);
+          if (event.deleted) {
+            this.storeHelpers.removeItemFromCollection(event, this._collections[modelName]);
+          }
+          else {
+            this.storeHelpers.updateCollection(event, this._collections[modelName]);
+          }
         },
         err => this.$log.error
       );
diff --git a/src/app/datasources/websocket/global.ts b/src/app/datasources/websocket/global.ts
index e86282a..8774173 100644
--- a/src/app/datasources/websocket/global.ts
+++ b/src/app/datasources/websocket/global.ts
@@ -24,6 +24,7 @@
 export interface IWSEvent {
   model: string;
   skip_notification?: boolean;
+  deleted?: boolean;
   msg: {
     changed_fields: string[],
     object?: any,
@@ -53,7 +54,15 @@
     const ignoredFields: string[] = ['created', 'updated', 'backend_register'];
 
     this.socket = io(this.AppConfig.websocketClient);
-    this.socket.on('event', (data: IWSEvent): void => {
+
+    this.socket.on('remove', (data: IWSEvent): void => {
+      this.$log.info(`[WebSocket] Received Remove Event for: ${data.model} [${data.msg.pk}]`, data);
+      this._events.next(data);
+
+      // TODO update observers of parent classes
+    });
+
+    this.socket.on('update', (data: IWSEvent): void => {
 
         if (data.msg.changed_fields.length === 0 || _.intersection(data.msg.changed_fields, ignoredFields).length === data.msg.changed_fields.length) {
           // NOTE means that the only updated fields does not change anything in the UI, so don't send events around