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;
+}