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 IAgentStatus {
27 WS_connected: boolean;
28 connectionRetry: number;
33 const DEFAULT_PORT = 8010;
34 const DEFAULT_API_KEY = "1234abcezam";
35 const API_VERSION = "v1";
38 export class XDSAgentService {
39 public Status$: Observable<IAgentStatus>;
41 private baseRestUrl: string;
42 private wsUrl: string;
43 private connectionMaxRetry: number;
44 private apikey: string;
45 private _status: IAgentStatus = {
52 private statusSubject = <BehaviorSubject<IAgentStatus>>new BehaviorSubject(this._status);
55 private socket: SocketIOClient.Socket;
57 constructor(private http: Http, private _window: Window, private alert: AlertService) {
59 this.Status$ = this.statusSubject.asObservable();
61 this.apikey = DEFAULT_API_KEY; // FIXME Add dynamic allocated key
62 this._initURLs('http://localhost:' + DEFAULT_PORT);
65 connect(retry: number, url?: string): Observable<IAgentStatus> {
69 //FIXME [XDS-Agent]: not implemented yet, set always as connected
70 //this._status.connected = false;
71 this._status.connected = true;
72 this._status.connectionRetry = 0;
73 this.connectionMaxRetry = retry || 3600; // 1 hour
75 // Init IO Socket connection
76 this._handleIoSocket();
78 // Get Version in order to check connection via a REST request
79 return this.getVersion()
81 this._status.version = v.version;
82 this.statusSubject.next(Object.assign({}, this._status));
87 public getVersion(): Observable<IXDSVersion> {
88 /*FIXME [XDS-Agent]: Not implemented for now
89 return this._get('/version');
91 return Observable.of({
92 version: "NOT_IMPLEMENTED",
93 apiVersion: "NOT_IMPLEMENTED",
94 gitTag: "NOT_IMPLEMENTED"
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('Access-Control-Allow-Origin', '*');
143 headers.append('Accept', 'application/json');
144 headers.append('Content-Type', 'application/json');
145 if (this.apikey !== "") {
146 headers.append('X-API-Key', this.apikey);
150 options.headers = headers;
154 private _checkAlive(): Observable<boolean> {
155 if (this._status.connected) {
156 return Observable.of(true);
159 return this.http.get(this._status.baseURL, this._attachAuthHeaders())
160 .map((r) => this._status.connected = true)
161 .retryWhen((attempts) => {
162 this._status.connectionRetry = 0;
163 return attempts.flatMap(error => {
164 this._status.connected = false;
165 if (++this._status.connectionRetry >= this.connectionMaxRetry) {
166 return Observable.throw("XDS local Agent not responding (url=" + this._status.baseURL + ")");
168 return Observable.timer(1000);
174 private _get(url: string): Observable<any> {
175 return this._checkAlive()
176 .flatMap(() => this.http.get(this.baseRestUrl + url, this._attachAuthHeaders()))
177 .map((res: Response) => res.json())
178 .catch(this._decodeError);
180 private _post(url: string, body: any): Observable<any> {
181 return this._checkAlive()
182 .flatMap(() => this.http.post(this.baseRestUrl + url, JSON.stringify(body), this._attachAuthHeaders()))
183 .map((res: Response) => res.json())
185 return this._decodeError(error);
188 private _delete(url: string): Observable<any> {
189 return this._checkAlive()
190 .flatMap(() => this.http.delete(this.baseRestUrl + url, this._attachAuthHeaders()))
191 .map((res: Response) => res.json())
192 .catch(this._decodeError);
195 private _decodeError(err: Response | any) {
198 this._status.connected = false;
200 if (typeof err === "object") {
201 if (err.statusText) {
203 } else if (err.error) {
204 e = String(err.error);
206 e = JSON.stringify(err);
208 } else if (err instanceof Response) {
209 const body = err.json() || 'Server error';
210 const error = body.error || JSON.stringify(body);
211 e = `${err.status} - ${err.statusText || ''} ${error}`;
213 e = err.message ? err.message : err.toString();
215 return Observable.throw(e);