Updating observable on webscoket event

Change-Id: I8325785b8d40b646ea67f28b61b8d603803a571e
diff --git a/package.json b/package.json
index 929c6d4..796b41f 100644
--- a/package.json
+++ b/package.json
@@ -8,9 +8,12 @@
     "@angular/platform-browser": "^2.0.0",
     "@angular/platform-browser-dynamic": "^2.0.0",
     "@angular/router": "^3.0.0",
+    "@types/lodash": "^4.14.42",
     "angular2-cookie": "^1.2.5",
     "core-js": "^2.4.1",
+    "lodash": "^4.17.2",
     "rxjs": "5.0.0-beta.12",
+    "socket.io-client": "^1.7.1",
     "zone.js": "^0.6.21"
   },
   "devDependencies": {
@@ -64,7 +67,7 @@
     "test": "gulp test",
     "test:auto": "gulp test:auto",
     "config": "gulp config",
-    "lint": "tslint ./src/**/*.ts"
+    "lint": "tslint -c ./tslint.json ./src/**/*.ts"
   },
   "eslintConfig": {
     "root": true,
diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts
index 413290c..05cc11d 100644
--- a/src/app/components/login/login.component.ts
+++ b/src/app/components/login/login.component.ts
@@ -5,6 +5,8 @@
 import {StyleConfig} from '../../config/style.config';
 import {AuthService} from '../../services/rest/auth.service';
 
+import {AppConfig} from '../../config/app.config';
+
 export class Auth {
   constructor(
     public username,
diff --git a/src/app/hello.html b/src/app/hello.html
index 44631ae..5652063 100644
--- a/src/app/hello.html
+++ b/src/app/hello.html
@@ -4,4 +4,9 @@
 
 <xos-logout></xos-logout>
 
-<pre>{{ endpoints | json }}</pre>
+<div *ngFor="let instance of instances">
+  <b>{{instance.name}}</b>
+  <i>{{instance.isolation}}</i>
+  {{instance.slice}}
+  {{instance.node}}
+</div>
diff --git a/src/app/hello.spec.ts b/src/app/hello.spec.ts
index 30e8e79..a0cc5a4 100644
--- a/src/app/hello.spec.ts
+++ b/src/app/hello.spec.ts
@@ -8,6 +8,11 @@
 import { MockBackend } from '@angular/http/testing';
 import {CookieService} from 'angular2-cookie/services/cookies.service';
 import {Router} from '@angular/router';
+import {XosHttp} from './services/rest/xoshttp.service';
+import {InstanceStore} from './services/stores/instance.store';
+import {GlobalEvent} from './services/websockets/websocket.global';
+import {AuthService} from './services/rest/auth.service';
+import {InstanceService} from './services/rest/instance.service';
 
 describe('hello component', () => {
   beforeEach(async(() => {
@@ -30,7 +35,12 @@
         {
           provide: Router,
           useClass: class { navigate = jasmine.createSpy('navigate'); }
-        }
+        },
+        XosHttp,
+        InstanceStore,
+        GlobalEvent,
+        AuthService,
+        InstanceService
       ]
     });
     TestBed.compileComponents();
diff --git a/src/app/hello.ts b/src/app/hello.ts
index 554b2de..8757cc3 100644
--- a/src/app/hello.ts
+++ b/src/app/hello.ts
@@ -2,31 +2,39 @@
 import {Component, OnInit} from '@angular/core';
 import {StyleConfig} from './config/style.config';
 import {CoreService} from './services/rest/core.service';
+import {InstanceStore} from './services/stores/instance.store';
+import {IInstance} from './interfaces/instance.interface';
 
 @Component({
   selector: 'xos-app',
   template: require('./hello.html'),
-  providers: [CoreService],
+  providers: [CoreService, InstanceStore],
 })
 export class HelloComponent implements OnInit {
 
   // declare class properties
   public hello: string;
-  public endpoints: any[];
+  public instances: IInstance[];
 
-  constructor(private coreService: CoreService) {
+  constructor(
+    private coreService: CoreService,
+    private instanceStore: InstanceStore
+  ) {
     this.hello = `Hello ${StyleConfig.projectName}!`;
-    this.endpoints = [];
+    this.instances = [];
   }
 
   ngOnInit() {
     console.log('on init');
-    this.coreService.getCoreEndpoints()
+    this.instanceStore.query()
       .subscribe(
-        endpoints => {
-          this.endpoints = endpoints;
+        instances => {
+          console.log(instances);
+          this.instances = instances;
         },
-        err => console.log
+        err => {
+          console.warn(err);
+        }
       );
   }
 }
diff --git a/src/app/index.ts b/src/app/index.ts
index af804d3..b44673a 100644
--- a/src/app/index.ts
+++ b/src/app/index.ts
@@ -6,10 +6,19 @@
 
 import {routing, RootComponent} from './routes';
 
+// registering components
 import {HelloComponent} from './hello';
 import {LoginComponent} from './components/login/login.component';
+import {LogoutComponent} from './components/logout/logout.component';
+
+// registering directives
 import {ProtectedDirective} from './directives/protected.directive';
-import {LogoutComponent} from './components/logout/logout.component.ts';
+
+// registering services
+import {AuthService} from './services/rest/auth.service';
+import {XosHttp} from './services/rest/xoshttp.service';
+import {InstanceService} from './services/rest/instance.service';
+import {GlobalEvent} from './services/websockets/websocket.global';
 
 @NgModule({
   imports: [
@@ -25,7 +34,13 @@
     LogoutComponent,
     ProtectedDirective
   ],
-  providers: [CookieService],
+  providers: [
+    CookieService,
+    AuthService,
+    XosHttp,
+    InstanceService,
+    GlobalEvent
+  ],
   bootstrap: [RootComponent]
 })
 export class AppModule {}
diff --git a/src/app/interfaces/instance.interface.ts b/src/app/interfaces/instance.interface.ts
new file mode 100644
index 0000000..29a37f2
--- /dev/null
+++ b/src/app/interfaces/instance.interface.ts
@@ -0,0 +1,6 @@
+export interface IInstance {
+  id: number;
+  name: string;
+  backend_status: string;
+  created: string;
+}
diff --git a/src/app/interfaces/ws.interface.ts b/src/app/interfaces/ws.interface.ts
new file mode 100644
index 0000000..05de51b
--- /dev/null
+++ b/src/app/interfaces/ws.interface.ts
@@ -0,0 +1,8 @@
+export interface IWSEvent {
+ model: string;
+ msg: {
+   changed_fields: string[],
+   object?: any,
+   pk?: number
+ }
+}
diff --git a/src/app/services/rest/auth.service.ts b/src/app/services/rest/auth.service.ts
index c53e3f4..b5b00fb 100644
--- a/src/app/services/rest/auth.service.ts
+++ b/src/app/services/rest/auth.service.ts
@@ -3,7 +3,7 @@
 // Imports
 import {AppConfig} from '../../config/app.config';
 import {Injectable}     from '@angular/core';
-import {Http, Response} from '@angular/http';
+import {Http, Response, Headers} from '@angular/http';
 import {Observable} from 'rxjs/Rx';
 import {IAuthRequest, IAuthResponse} from '../../interfaces/auth.interface';
 import {CookieService} from 'angular2-cookie/core';
@@ -24,17 +24,25 @@
   isAuthenticated(): string {
     this.xosToken = this.cookieService.get('xoscsrftoken');
     this.xosSessionId = this.cookieService.get('xossessionid');
-    return this.xosToken;
+    return this.xosToken && this.xosSessionId;
+  }
+
+  // get auth info to authenticate API
+  getUserHeaders(): Headers{
+    const headers = new Headers();
+    headers.append('x-csrftoken', this.cookieService.get('xoscsrftoken'));
+    headers.append('x-sessionid', this.cookieService.get('xossessionid'));
+    return headers;
   }
 
   // save cookies
-  storeAuth(auth: IAuthResponse): void {
+  private storeAuth(auth: IAuthResponse): void {
     this.cookieService.put('xoscsrftoken', auth.xoscsrftoken);
     this.cookieService.put('xossessionid', auth.xossessionid);
   }
 
   // remove cookies
-  removeAuth(): void {
+  private removeAuth(): void {
     this.cookieService.remove('xoscsrftoken');
     this.cookieService.remove('xossessionid');
   }
diff --git a/src/app/services/rest/core.service.ts b/src/app/services/rest/core.service.ts
index 04097a6..3890c47 100644
--- a/src/app/services/rest/core.service.ts
+++ b/src/app/services/rest/core.service.ts
@@ -2,9 +2,10 @@
 
 // Imports
 import {AppConfig} from '../../config/app.config';
-import { Injectable }     from '@angular/core';
-import { Http, Response, Headers, RequestOptions } from '@angular/http';
+import {Injectable}     from '@angular/core';
+import {Response} from '@angular/http';
 import {Observable} from 'rxjs/Rx';
+import {XosHttp} from './xoshttp.service';
 
 // Import RxJs required methods
 import 'rxjs/add/operator/map';
@@ -12,19 +13,22 @@
 
 @Injectable()
 export class CoreService {
-  // Resolve HTTP using the constructor
-  constructor (private http: Http) {}
   // private instance variable to hold base url
   private baseUrl = AppConfig.apiEndpoint;
 
+  // Resolve HTTP using the constructor
+  constructor (private http: XosHttp) {}
+
   // Fetch all existing comments
   getCoreEndpoints() : Observable<any[]> {
 
+    const search = 'some=param';
+
     // ...using get request
-    return this.http.get(`${this.baseUrl}/core/`)
+    return this.http.get(`${this.baseUrl}/core/`, {search})
     // ...and calling .json() on the response to return data
-      .map((res:Response) => res.json())
-      //...errors if any
+      .map((res: Response) => res.json())
+      // ...errors if any
       .catch((error:any) => Observable.throw(error.json().error || 'Server error'));
 
   }
diff --git a/src/app/services/rest/instance.service.ts b/src/app/services/rest/instance.service.ts
new file mode 100644
index 0000000..c864e54
--- /dev/null
+++ b/src/app/services/rest/instance.service.ts
@@ -0,0 +1,25 @@
+/// <reference path="../../../../typings/index.d.ts"/>
+
+// Imports
+import {AppConfig} from '../../config/app.config';
+import {AuthService} from './auth.service';
+import { Injectable }     from '@angular/core';
+import { Http, Response, Headers } from '@angular/http';
+import {XosHttp} from './xoshttp.service';
+import {Observable} from 'rxjs/Rx';
+
+// Import RxJs required methods
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/catch';
+
+@Injectable()
+export class InstanceService {
+  private baseUrl = AppConfig.apiEndpoint;
+  constructor (private http: XosHttp, private authService: AuthService) {}
+  // Fetch all existing instances
+  query() : Observable<any[]> {
+    return this.http.get(`${this.baseUrl}/core/instances/`)
+      .map((res: Response) => res.json())
+      .catch((error: any) => Observable.throw(error.response.json().error || 'Server error'));
+  }
+}
diff --git a/src/app/services/rest/xoshttp.service.ts b/src/app/services/rest/xoshttp.service.ts
new file mode 100644
index 0000000..5572683
--- /dev/null
+++ b/src/app/services/rest/xoshttp.service.ts
@@ -0,0 +1,55 @@
+import {Http, RequestOptionsArgs, Response, URLSearchParams} from '@angular/http';
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs';
+import {AuthService} from './auth.service';
+/**
+ * Created by teone on 12/6/16.
+ */
+@Injectable()
+export class XosHttp {
+  constructor(
+    private http: Http,
+    private authService: AuthService
+  ) {
+  }
+
+  private checkOptions(options?: RequestOptionsArgs): RequestOptionsArgs {
+    // if options are not there, create them
+    if(!options){
+      options = {};
+    }
+    return options;
+  }
+
+  private getHeaders(options: RequestOptionsArgs): RequestOptionsArgs {
+    // add auth headers
+    options.headers = this.authService.getUserHeaders();
+    return options;
+  }
+
+  private getParams(options: RequestOptionsArgs): RequestOptionsArgs {
+    // add the no_hyperlinks param
+    if (!options.search) {
+      options.search = new URLSearchParams();
+    }
+
+    if (options.search instanceof URLSearchParams) {
+      options.search.set('no_hyperlinks', '1');
+    }
+    else if (typeof options.search === 'string') {
+      options.search += '&no_hyperlinks=1';
+    }
+    return options;
+  }
+
+  get(url: string, options?: RequestOptionsArgs): Observable<Response> {
+
+    options = this.checkOptions(options);
+    options = this.getHeaders(options);
+    options = this.getParams(options);
+
+    return this.http.get(url, options)
+  }
+
+  // TODO add POST, PUT, DELETE declaration
+}
diff --git a/src/app/services/stores/instance.store.ts b/src/app/services/stores/instance.store.ts
new file mode 100644
index 0000000..a87630c
--- /dev/null
+++ b/src/app/services/stores/instance.store.ts
@@ -0,0 +1,54 @@
+/// <reference path="../../../../typings/index.d.ts"/>
+
+import {Injectable} from '@angular/core';
+import {BehaviorSubject} from 'rxjs/Rx';
+import {IInstance} from '../../interfaces/instance.interface';
+import {InstanceService} from '../rest/instance.service';
+import * as _ from 'lodash';
+import {IWSEvent} from '../../interfaces/ws.interface';
+import {GlobalEvent} from '../websockets/websocket.global';
+
+@Injectable()
+export class InstanceStore {
+  private _instances: BehaviorSubject<IInstance[]> = new BehaviorSubject([]);
+  constructor(private instanceService: InstanceService, private globalEvent: GlobalEvent) {
+    this.loadInitialData();
+    this.globalEvent.list()
+      .filter((e: IWSEvent) => {
+        console.log('filter', e);
+        return e.model === 'Instance';
+      })
+      .subscribe(
+        (event: IWSEvent) => {
+
+          const collection = this._instances.value;
+
+          const exist = _.find(collection, (i) => {
+            return i.id === event.msg.object.id;
+          });
+
+          // remove in order to update
+          if (exist) {
+            _.remove(collection, {id: event.msg.object.id});
+          }
+          collection.push(event.msg.object);
+          this._instances.next(collection);
+        }
+      );
+  }
+
+  loadInitialData() {
+    this.instanceService.query()
+      .subscribe(
+        res => {
+          this._instances.next(res);
+        },
+        err => console.log('Error retrieving Instances', err)
+      );
+  }
+
+  query() {
+    return this._instances.asObservable();
+  }
+
+}
diff --git a/src/app/services/websockets/websocket.global.ts b/src/app/services/websockets/websocket.global.ts
new file mode 100644
index 0000000..ee62c12
--- /dev/null
+++ b/src/app/services/websockets/websocket.global.ts
@@ -0,0 +1,29 @@
+/// <reference path="../../../../typings/index.d.ts"/>
+
+import {Injectable} from '@angular/core';
+import {BehaviorSubject} from 'rxjs/Rx';
+import * as io from 'socket.io-client';
+import {AppConfig} from '../../config/app.config';
+import {IWSEvent} from '../../interfaces/ws.interface';
+
+@Injectable()
+export class GlobalEvent {
+  private _events: BehaviorSubject<IWSEvent> = new BehaviorSubject<IWSEvent>({
+    model: 'XOS',
+    msg: {
+      changed_fields: []
+    }
+  });
+  private socket;
+  constructor() {
+    this.socket = io(AppConfig.websocketClient);
+    this.socket.on('event', (data: IWSEvent) => {
+      this._events.next(data);
+    });
+  }
+
+  list() {
+    return this._events.asObservable();
+  }
+
+}
diff --git a/tslint.json b/tslint.json
index d8902fa..5a95833 100644
--- a/tslint.json
+++ b/tslint.json
@@ -28,8 +28,7 @@
     ],
     "class-name": true,
     "comment-format": [true,
-      "check-space",
-      "check-lowercase"
+      "check-space"
     ],
     "curly": true,
     "eofline": true,
@@ -73,7 +72,6 @@
     "one-line": [true,
       "check-open-brace",
       "check-catch",
-      "check-else",
       "check-whitespace"
     ],
     "quotemark": [true, "single"],
diff --git a/typings/index.d.ts b/typings/index.d.ts
index 2613c55..8b5b6b4 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -1,3 +1,8 @@
 /// <reference path="globals/es6-shim/index.d.ts" />
 /// <reference path="globals/jasmine/index.d.ts" />
 /// <reference path="globals/require/index.d.ts" />
+
+declare module 'socket.io-client' {
+  var e: any;
+  export = e;
+}