1 import { Injectable } from '@angular/core';
2 import { Http, Headers, RequestOptionsArgs, Response } from '@angular/http';
3 import { Location } from '@angular/common';
4 import { Observable } from 'rxjs/Observable';
5 import { Subject } from 'rxjs/Subject';
6 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
7 import * as io from 'socket.io-client';
9 import { AlertService } from './alert.service';
12 // Import RxJs required methods
13 import 'rxjs/add/operator/map';
14 import 'rxjs/add/operator/catch';
15 import 'rxjs/add/observable/throw';
17 export interface IXDSVersion {
24 export interface IXDSDeploy {
29 export interface IAgentStatus {
32 WS_connected: boolean;
33 connectionRetry: number;
38 const DEFAULT_PORT = 8010;
39 const DEFAULT_API_KEY = "1234abcezam";
40 const API_VERSION = "v1";
43 export class XDSAgentService {
44 public Status$: Observable<IAgentStatus>;
46 private baseRestUrl: string;
47 private wsUrl: string;
48 private connectionMaxRetry: number;
49 private apikey: string;
50 private _status: IAgentStatus = {
57 private statusSubject = <BehaviorSubject<IAgentStatus>>new BehaviorSubject(this._status);
60 private socket: SocketIOClient.Socket;
62 constructor(private http: Http, private _window: Window, private alert: AlertService) {
64 this.Status$ = this.statusSubject.asObservable();
66 this.apikey = DEFAULT_API_KEY; // FIXME Add dynamic allocated key
67 this._initURLs('http://localhost:' + DEFAULT_PORT);
70 connect(retry: number, url?: string): Observable<IAgentStatus> {
74 this._status.connected = false;
75 this._status.connectionRetry = 0;
76 this.connectionMaxRetry = retry || 3600; // 1 hour
78 // Init IO Socket connection
79 this._handleIoSocket();
81 // Get Version in order to check connection via a REST request
82 return this.getVersion()
84 this._status.version = v.version;
85 this.statusSubject.next(Object.assign({}, this._status));
90 public getVersion(): Observable<IXDSVersion> {
91 return this._get('/version');
94 public deploy(dpy: IXDSDeploy) {
95 return this._post('/deploy', dpy);
98 private _initURLs(url: string) {
99 this._status.baseURL = url;
100 this.baseRestUrl = this._status.baseURL + '/api/' + API_VERSION;
101 let re = this._status.baseURL.match(/http[s]?:\/\/([^\/]*)[\/]?/);
102 if (re === null || re.length < 2) {
104 console.error('ERROR: cannot determine Websocket url');
107 this.wsUrl = 'ws://' + re[1];
110 private _WSState(sts: boolean) {
111 this._status.WS_connected = sts;
112 this.statusSubject.next(Object.assign({}, this._status));
115 private _handleIoSocket() {
116 this.socket = io(this.wsUrl, { transports: ['websocket'] });
118 this.socket.on('connect_error', (res) => {
119 this._WSState(false);
120 console.error('WS Connect_error ', res);
123 this.socket.on('connect', (res) => {
127 this.socket.on('disconnection', (res) => {
128 this._WSState(false);
129 this.alert.error('WS disconnection: ' + res);
132 this.socket.on('error', (err) => {
133 console.error('WS error:', err);
138 private _attachAuthHeaders(options?: any) {
139 options = options || {};
140 let headers = options.headers || new Headers();
141 // headers.append('Authorization', 'Basic ' + btoa('username:password'));
142 headers.append('Accept', 'application/json');
143 headers.append('Content-Type', 'application/json');
144 if (this.apikey !== "") {
145 headers.append('X-API-Key', this.apikey);
149 options.headers = headers;
153 private _checkAlive(): Observable<boolean> {
154 if (this._status.connected) {
155 return Observable.of(true);
158 return this.http.get(this.baseRestUrl + "/version", this._attachAuthHeaders())
159 .map((r) => this._status.connected = true)
160 .retryWhen((attempts) => {
161 this._status.connectionRetry = 0;
162 return attempts.flatMap(error => {
163 this._status.connected = false;
164 if (++this._status.connectionRetry >= this.connectionMaxRetry) {
165 return Observable.throw("XDS local Agent not responding (url=" + this._status.baseURL + ")");
167 return Observable.timer(1000);
173 private _get(url: string): Observable<any> {
174 return this._checkAlive()
175 .flatMap(() => this.http.get(this.baseRestUrl + url, this._attachAuthHeaders()))
176 .map((res: Response) => res.json())
177 .catch(this._decodeError);
179 private _post(url: string, body: any): Observable<any> {
180 return this._checkAlive()
181 .flatMap(() => this.http.post(this.baseRestUrl + url, JSON.stringify(body), this._attachAuthHeaders()))
182 .map((res: Response) => res.json())
183 .catch(this._decodeError);
185 private _delete(url: string): Observable<any> {
186 return this._checkAlive()
187 .flatMap(() => this.http.delete(this.baseRestUrl + url, this._attachAuthHeaders()))
188 .map((res: Response) => res.json())
189 .catch(this._decodeError);
192 private _decodeError(err: Response | any) {
195 this._status.connected = false;
197 if (err instanceof Response) {
198 const body = err.json() || 'Server error';
199 e = body.error || JSON.stringify(body);
200 if (!e || e === "") {
201 e = `${err.status} - ${err.statusText || 'Unknown error'}`;
203 } else if (typeof err === "object") {
204 if (err.statusText) {
206 } else if (err.error) {
207 e = String(err.error);
209 e = JSON.stringify(err);
212 e = err.message ? err.message : err.toString();
214 return Observable.throw(e);