adbee9885f433c3f652ae808b7317c42fa959771
[src/xds/xds-agent.git] / webapp / src / app / @core-xds / services / xdsagent.service.ts
1 /**
2 * @license
3 * Copyright (C) 2017-2018 "IoT.bzh"
4 * Author Sebastien Douheret <sebastien@iot.bzh>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 import { Injectable, Inject, isDevMode } from '@angular/core';
20 import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
21 import { DOCUMENT } from '@angular/common';
22 import { Observable } from 'rxjs/Observable';
23 import { Subject } from 'rxjs/Subject';
24 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
25 import * as io from 'socket.io-client';
26
27 import { AlertService } from './alert.service';
28 import { ISdk, ISdkManagementMsg } from './sdk.service';
29 import { ProjectType, ProjectTypeEnum } from './project.service';
30 import { TargetType, TargetTypeEnum } from './target.service';
31
32 // Import RxJs required methods
33 import 'rxjs/add/operator/map';
34 import 'rxjs/add/operator/catch';
35 import 'rxjs/add/observable/throw';
36 import 'rxjs/add/operator/mergeMap';
37 import 'rxjs/add/observable/of';
38 import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
39
40
41 export interface IXDSConfigProject {
42   id: string;
43   path: string;
44   clientSyncThingID: string;
45   type: string;
46   label?: string;
47   defaultSdkID?: string;
48 }
49
50 interface IXDSBuilderConfig {
51   ip: string;
52   port: string;
53   syncThingID: string;
54 }
55
56 export interface IXDSProjectConfig {
57   id: string;
58   serverId: string;
59   label: string;
60   clientPath: string;
61   serverPath?: string;
62   type: ProjectTypeEnum;
63   status?: string;
64   isInSync?: boolean;
65   defaultSdkID: string;
66   clientData?: string;
67 }
68
69 /** Targets **/
70 export interface IXDSTargetConfig {
71   id?: string;
72   name: string;
73   type: TargetTypeEnum;
74   ip: string;
75   status?: string;
76   terms?: IXDSTargetTerminal[];
77 }
78
79 export interface IXDSTargetTerminal {
80   id?: string;
81   type: string;
82   name: string;
83   status?: string;
84   cols?: number;
85   rows?: number;
86 }
87
88 export interface IXDSVer {
89   id: string;
90   version: string;
91   apiVersion: string;
92   gitTag: string;
93 }
94
95 export interface IXDSVersions {
96   client: IXDSVer;
97   servers: IXDSVer[];
98 }
99
100 export interface IXDServerCfg {
101   id: string;
102   url: string;
103   apiUrl?: string;
104   partialUrl?: string;
105   connRetry: number;
106   connected: boolean;
107 }
108
109 export interface IXDSConfig {
110   servers: IXDServerCfg[];
111 }
112
113 export interface ISdkMessage {
114   wsID: string;
115   msgType: string;
116   data: any;
117 }
118
119 export interface ICmdOutput {
120   cmdID: string;
121   timestamp: string;
122   stdout: string;
123   stderr: string;
124 }
125
126 export interface ICmdExit {
127   cmdID: string;
128   timestamp: string;
129   code: number;
130   error: string;
131 }
132
133 export interface IServerStatus {
134   id: string;
135   connected: boolean;
136 }
137
138 export interface IAgentStatus {
139   connected: boolean;
140   servers: IServerStatus[];
141 }
142
143
144 @Injectable()
145 export class XDSAgentService {
146
147   public Socket: SocketIOClient.Socket;
148   public XdsConfig$: Observable<IXDSConfig>;
149   public Status$: Observable<IAgentStatus>;
150   public CmdOutput$ = <Subject<ICmdOutput>>new Subject();
151   public CmdExit$ = <Subject<ICmdExit>>new Subject();
152
153   protected sockConnect$ = new Subject<SocketIOClient.Socket>();
154   protected sockDisconnect$ = new Subject<SocketIOClient.Socket>();
155
156   protected projectAdd$ = new Subject<IXDSProjectConfig>();
157   protected projectDel$ = new Subject<IXDSProjectConfig>();
158   protected projectChange$ = new Subject<IXDSProjectConfig>();
159
160   protected sdkAdd$ = new Subject<ISdk>();
161   protected sdkRemove$ = new Subject<ISdk>();
162   protected sdkChange$ = new Subject<ISdk>();
163   protected sdkManagement$ = new Subject<ISdkManagementMsg>();
164
165   protected targetAdd$ = new Subject<IXDSTargetConfig>();
166   protected targetDel$ = new Subject<IXDSTargetConfig>();
167   protected targetChange$ = new Subject<IXDSTargetConfig>();
168
169   protected targetTerminalAdd$ = new Subject<IXDSTargetTerminal>();
170   protected targetTerminalDel$ = new Subject<IXDSTargetTerminal>();
171   protected targetTerminalChange$ = new Subject<IXDSTargetTerminal>();
172
173   private _socket: SocketIOClient.Socket;
174   private baseUrl: string;
175   private wsUrl: string;
176   private httpSessionID: string;
177   private _config = <IXDSConfig>{ servers: [] };
178   private _status = { connected: false, servers: [] };
179
180   private configSubject = <BehaviorSubject<IXDSConfig>>new BehaviorSubject(this._config);
181   private statusSubject = <BehaviorSubject<IAgentStatus>>new BehaviorSubject(this._status);
182
183
184
185   constructor(@Inject(DOCUMENT) private document: Document,
186     private http: HttpClient, private alert: AlertService) {
187
188     this.XdsConfig$ = this.configSubject.asObservable();
189     this.Status$ = this.statusSubject.asObservable();
190
191     const originUrl = this.document.location.origin;
192     this.baseUrl = originUrl + '/api/v1';
193
194     // Retrieve Session ID / token
195     this.http.get(this.baseUrl + '/version', { observe: 'response' })
196       .subscribe(
197         resp => {
198           this.httpSessionID = resp.headers.get('xds-agent-sid');
199
200           const re = originUrl.match(/http[s]?:\/\/([^\/]*)[\/]?/);
201           if (re === null || re.length < 2) {
202             console.error('ERROR: cannot determine Websocket url');
203           } else {
204             this.wsUrl = 'ws://' + re[1];
205             this._handleIoSocket();
206             this._RegisterEvents();
207           }
208         },
209         err => {
210           /* tslint:disable:no-console */
211           console.error('ERROR while retrieving session id:', err);
212         });
213   }
214
215   private _NotifyXdsAgentState(sts: boolean) {
216     this._status.connected = sts;
217     this.statusSubject.next(Object.assign({}, this._status));
218
219     // Update XDS config including XDS Server list when connected
220     if (sts) {
221       this.getConfig().subscribe(c => {
222         this._config = c;
223         this._NotifyXdsServerState();
224         this.configSubject.next(Object.assign({ servers: [] }, this._config));
225       });
226     }
227   }
228
229   private _NotifyXdsServerState() {
230     this._status.servers = this._config.servers.map(svr => {
231       return { id: svr.id, connected: svr.connected };
232     });
233     this.statusSubject.next(Object.assign({}, this._status));
234   }
235
236   private _handleIoSocket() {
237     this.Socket = this._socket = io(this.wsUrl, { transports: ['websocket'] });
238
239     this._socket.on('connect_error', (res) => {
240       this._NotifyXdsAgentState(false);
241       console.error('XDS Agent WebSocket Connection error !');
242     });
243
244     this._socket.on('connect', (res) => {
245       this._NotifyXdsAgentState(true);
246       this.sockConnect$.next(this._socket);
247     });
248
249     this._socket.on('disconnection', (res) => {
250       this._NotifyXdsAgentState(false);
251       this.alert.error('WS disconnection: ' + res);
252       this.sockDisconnect$.next(this._socket);
253     });
254
255     this._socket.on('error', (err) => {
256       console.error('WS error:', err);
257     });
258
259     // XDS Events decoding
260
261     this._socket.on('exec:output', data => {
262       this.CmdOutput$.next(Object.assign({}, <ICmdOutput>data));
263     });
264
265     this._socket.on('exec:exit', data => {
266       this.CmdExit$.next(Object.assign({}, <ICmdExit>data));
267     });
268
269     this._socket.on('event:server-config', ev => {
270       if (ev && ev.data) {
271         const cfg: IXDServerCfg = ev.data;
272         const idx = this._config.servers.findIndex(el => el.id === cfg.id);
273         if (idx >= 0) {
274           this._config.servers[idx] = Object.assign({}, cfg);
275           this._NotifyXdsServerState();
276         }
277         this.configSubject.next(Object.assign({}, this._config));
278       }
279     });
280
281     /*** Project events ****/
282
283     this._socket.on('event:project-add', (ev) => {
284       if (ev && ev.data && ev.data.id) {
285         this.projectAdd$.next(Object.assign({}, ev.data));
286         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && ev.data.label) {
287           this.alert.info('Project "' + ev.data.label + '" has been added by another tool.');
288         }
289       } else if (isDevMode) {
290         /* tslint:disable:no-console */
291         console.log('Warning: received event:project-add with unknown data: ev=', ev);
292       }
293     });
294
295     this._socket.on('event:project-delete', (ev) => {
296       if (ev && ev.data && ev.data.id) {
297         this.projectDel$.next(Object.assign({}, ev.data));
298         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && ev.data.label) {
299           this.alert.info('Project "' + ev.data.label + '" has been deleted by another tool.');
300         }
301       } else if (isDevMode) {
302         console.log('Warning: received event:project-delete with unknown data: ev=', ev);
303       }
304     });
305
306     this._socket.on('event:project-state-change', ev => {
307       if (ev && ev.data) {
308         this.projectChange$.next(Object.assign({}, ev.data));
309       } else if (isDevMode) {
310         console.log('Warning: received event:project-state-change with unknown data: ev=', ev);
311       }
312     });
313
314     /*** SDK Events ***/
315
316     this._socket.on('event:sdk-add', (ev) => {
317       if (ev && ev.data && ev.data.id) {
318         const evt = <ISdk>ev.data;
319         this.sdkAdd$.next(Object.assign({}, evt));
320
321         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && evt.name) {
322           this.alert.info('SDK "' + evt.name + '" has been added by another tool.');
323         }
324       } else if (isDevMode) {
325         console.log('Warning: received event:sdk-add with unknown data: ev=', ev);
326       }
327     });
328
329     this._socket.on('event:sdk-remove', (ev) => {
330       if (ev && ev.data && ev.data.id) {
331         const evt = <ISdk>ev.data;
332         this.sdkRemove$.next(Object.assign({}, evt));
333
334         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && evt.name) {
335           this.alert.info('SDK "' + evt.name + '" has been removed by another tool.');
336         }
337       } else if (isDevMode) {
338         console.log('Warning: received event:sdk-remove with unknown data: ev=', ev);
339       }
340     });
341
342     this._socket.on('event:sdk-state-change', (ev) => {
343       if (ev && ev.data && ev.data.id) {
344         const evt = <ISdk>ev.data;
345         this.sdkChange$.next(Object.assign({}, evt));
346
347       } else if (isDevMode) {
348         console.log('Warning: received event:sdk-state-change with unknown data: ev=', ev);
349       }
350     });
351
352     this._socket.on('event:sdk-management', (ev) => {
353       if (ev && ev.data && ev.data.sdk) {
354         const evt = <ISdkManagementMsg>ev.data;
355         this.sdkManagement$.next(Object.assign({}, evt));
356
357         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && evt.sdk.name) {
358           this.alert.info('SDK "' + evt.sdk.name + '" has been installed by another tool.');
359         }
360       } else if (isDevMode) {
361         /* tslint:disable:no-console */
362         console.log('Warning: received event:sdk-install with unknown data: ev=', ev);
363       }
364     });
365
366     /*** Target events ****/
367
368     this._socket.on('event:target-add', (ev) => {
369       if (ev && ev.data && ev.data.id) {
370         this.targetAdd$.next(Object.assign({}, ev.data));
371         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && ev.data.label) {
372           this.alert.info('Target "' + ev.data.label + '" has been added by another tool.');
373         }
374       } else if (isDevMode) {
375         /* tslint:disable:no-console */
376         console.log('Warning: received event:target-add with unknown data: ev=', ev);
377       }
378     });
379
380     this._socket.on('event:target-remove', (ev) => {
381       if (ev && ev.data && ev.data.id) {
382         this.targetDel$.next(Object.assign({}, ev.data));
383         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && ev.data.label) {
384           this.alert.info('Target "' + ev.data.label + '" has been deleted by another tool.');
385         }
386       } else if (isDevMode) {
387         console.log('Warning: received event:target-remove with unknown data: ev=', ev);
388       }
389     });
390
391     this._socket.on('event:target-state-change', ev => {
392       if (ev && ev.data) {
393         this.targetChange$.next(Object.assign({}, ev.data));
394       } else if (isDevMode) {
395         console.log('Warning: received event:target-state-change with unknown data: ev=', ev);
396       }
397     });
398
399     /*** Target Terminal events ****/
400
401     this._socket.on('event:target-terminal-add', (ev) => {
402       if (ev && ev.data && ev.data.id) {
403         this.targetTerminalAdd$.next(Object.assign({}, ev.data));
404         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && ev.data.label) {
405           this.alert.info('Target terminal "' + ev.data.label + '" has been added by another tool.');
406         }
407       } else if (isDevMode) {
408         /* tslint:disable:no-console */
409         console.log('Warning: received event:target-terminal-add with unknown data: ev=', ev);
410       }
411     });
412
413     this._socket.on('event:target-terminal-delete', (ev) => {
414       if (ev && ev.data && ev.data.id) {
415         this.targetTerminalDel$.next(Object.assign({}, ev.data));
416         if (ev.sessionID !== '' && ev.sessionID !== this.httpSessionID && ev.data.label) {
417           this.alert.info('Target terminal "' + ev.data.label + '" has been deleted by another tool.');
418         }
419       } else if (isDevMode) {
420         console.log('Warning: received event:target-terminal-delete with unknown data: ev=', ev);
421       }
422     });
423
424     this._socket.on('event:target-terminal-state-change', ev => {
425       if (ev && ev.data) {
426         this.targetTerminalChange$.next(Object.assign({}, ev.data));
427       } else if (isDevMode) {
428         console.log('Warning: received event:target-terminal-state-change with unknown data: ev=', ev);
429       }
430     });
431
432   }
433
434   /**
435   ** Events registration
436   ***/
437
438   onSocketConnect(): Observable<any> {
439     return this.sockConnect$.asObservable();
440   }
441
442   onSocketDisconnect(): Observable<any> {
443     return this.sockDisconnect$.asObservable();
444   }
445
446   onProjectAdd(): Observable<IXDSProjectConfig> {
447     return this.projectAdd$.asObservable();
448   }
449
450   onProjectDelete(): Observable<IXDSProjectConfig> {
451     return this.projectDel$.asObservable();
452   }
453
454   onProjectChange(): Observable<IXDSProjectConfig> {
455     return this.projectChange$.asObservable();
456   }
457
458   onSdkAdd(): Observable<ISdk> {
459     return this.sdkAdd$.asObservable();
460   }
461
462   onSdkRemove(): Observable<ISdk> {
463     return this.sdkRemove$.asObservable();
464   }
465
466   onSdkChange(): Observable<ISdk> {
467     return this.sdkChange$.asObservable();
468   }
469
470   onSdkManagement(): Observable<ISdkManagementMsg> {
471     return this.sdkManagement$.asObservable();
472   }
473
474   onTargetAdd(): Observable<IXDSTargetConfig> {
475     return this.targetAdd$.asObservable();
476   }
477
478   onTargetDelete(): Observable<IXDSTargetConfig> {
479     return this.targetDel$.asObservable();
480   }
481
482   onTargetChange(): Observable<IXDSTargetConfig> {
483     return this.targetChange$.asObservable();
484   }
485
486   onTargetTerminalAdd(): Observable<IXDSTargetTerminal> {
487     return this.targetTerminalAdd$.asObservable();
488   }
489
490   onTargetTerminalDelete(): Observable<IXDSTargetTerminal> {
491     return this.targetTerminalDel$.asObservable();
492   }
493
494   onTargetTerminalChange(): Observable<IXDSTargetTerminal> {
495     return this.targetTerminalChange$.asObservable();
496   }
497
498   /**
499   ** Misc / Version
500   ***/
501   getVersion(): Observable<IXDSVersions> {
502     return this._get('/version');
503   }
504
505   /***
506   ** Config
507   ***/
508   getConfig(): Observable<IXDSConfig> {
509     return this._get('/config');
510   }
511
512   setConfig(cfg: IXDSConfig): Observable<IXDSConfig> {
513     return this._post('/config', cfg);
514   }
515
516   setServerRetry(serverID: string, retry: number): Observable<IXDSConfig> {
517     const svr = this._getServer(serverID);
518     if (!svr) {
519       return Observable.throw('Unknown server ID');
520     }
521     if (retry < 0 || Number.isNaN(retry) || retry == null) {
522       return Observable.throw('Not a valid number');
523     }
524     svr.connRetry = retry;
525     return this._setConfig();
526   }
527
528   setServerUrl(serverID: string, url: string, retry: number): Observable<IXDSConfig> {
529     const svr = this._getServer(serverID);
530     if (!svr) {
531       return Observable.throw('Unknown server ID');
532     }
533     svr.connected = false;
534     svr.url = url;
535     if (!Number.isNaN(retry) && retry > 0) {
536       svr.connRetry = retry;
537     }
538     this._NotifyXdsServerState();
539     return this._setConfig();
540   }
541
542   private _setConfig(): Observable<IXDSConfig> {
543     return this.setConfig(this._config)
544       .map(newCfg => {
545         this._config = newCfg;
546         this.configSubject.next(Object.assign({}, this._config));
547         return this._config;
548       });
549   }
550
551   /***
552   ** SDKs
553   ***/
554   getSdks(serverID: string): Observable<ISdk[]> {
555     const svr = this._getServer(serverID);
556     if (!svr || !svr.connected) {
557       return Observable.of([]);
558     }
559     return this._get(svr.partialUrl + '/sdks');
560   }
561
562   installSdk(serverID: string, id: string, filename?: string, force?: boolean): Observable<ISdk> {
563     return this._post(this._getServerUrl(serverID) + '/sdks', { id: id, filename: filename, force: force });
564   }
565
566   abortInstall(serverID: string, id: string): Observable<ISdk> {
567     return this._post(this._getServerUrl(serverID) + '/sdks/abortinstall', { id: id });
568   }
569
570   removeSdk(serverID: string, id: string): Observable<ISdk> {
571     return this._delete(this._getServerUrl(serverID) + '/sdks/' + id);
572   }
573
574
575   /***
576   ** Projects
577   ***/
578   getProjects(): Observable<IXDSProjectConfig[]> {
579     return this._get('/projects');
580   }
581
582   addProject(cfg: IXDSProjectConfig): Observable<IXDSProjectConfig> {
583     return this._post('/projects', cfg);
584   }
585
586   deleteProject(id: string): Observable<IXDSProjectConfig> {
587     return this._delete('/projects/' + id);
588   }
589
590   updateProject(cfg: IXDSProjectConfig): Observable<IXDSProjectConfig> {
591     return this._put('/projects/' + cfg.id, cfg);
592   }
593
594   syncProject(id: string): Observable<string> {
595     return this._post('/projects/sync/' + id, {});
596   }
597
598   /***
599   ** Exec
600   ***/
601   exec(prjID: string, dir: string, cmd: string, sdkid?: string, args?: string[], env?: string[]): Observable<any> {
602     return this._post('/exec',
603       {
604         id: prjID,
605         rpath: dir,
606         cmd: cmd,
607         sdkID: sdkid || '',
608         args: args || [],
609         env: env || [],
610       });
611   }
612
613
614   /***
615   ** Targets
616   ***/
617   getTargets(serverID: string): Observable<IXDSTargetConfig[]> {
618     return this._get(this._getServerUrl(serverID) + '/targets');
619   }
620
621   addTarget(serverID: string, cfg: IXDSTargetConfig): Observable<IXDSTargetConfig> {
622     return this._post(this._getServerUrl(serverID) + '/targets', cfg);
623   }
624
625   deleteTarget(serverID: string, id: string): Observable<IXDSTargetConfig> {
626     return this._delete(this._getServerUrl(serverID) + '/targets/' + id);
627   }
628
629   updateTarget(serverID: string, cfg: IXDSTargetConfig): Observable<IXDSTargetConfig> {
630     return this._put(this._getServerUrl(serverID) + '/targets/' + cfg.id, cfg);
631   }
632
633   /***
634   ** Terminals
635   ***/
636   getTerminalsTarget(serverID, targetID: string): Observable<IXDSTargetTerminal[]> {
637     return this._get(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals');
638   }
639
640   getTerminalTarget(serverID, targetID, termID: string): Observable<IXDSTargetTerminal> {
641     return this._get(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals/' + termID);
642   }
643
644   createTerminalTarget(serverID, targetID: string, cfg: IXDSTargetTerminal): Observable<IXDSTargetTerminal> {
645     return this._post(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals', cfg);
646   }
647
648   updateTerminalTarget(serverID, targetID: string, cfg: IXDSTargetTerminal): Observable<IXDSTargetTerminal> {
649     if (cfg && (cfg.id !== '' || cfg.id !== undefined)) {
650       return this._put(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals/' + cfg.id, cfg);
651     }
652     return Observable.throw('Undefined terminal id');
653   }
654
655   openTerminalTarget(serverID, targetID, termID: string): Observable<IXDSTargetTerminal> {
656     return this._post(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals/' + termID + '/open', {});
657   }
658
659   closeTerminalTarget(serverID, targetID, termID: string): Observable<IXDSTargetTerminal> {
660     return this._post(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals/' + termID + '/close', {});
661   }
662
663   resizeTerminalTarget(serverID, targetID, termID: string, cols, rows: number): Observable<IXDSTargetTerminal> {
664     return this._post(this._getServerUrl(serverID) + '/targets/' + targetID + '/terminals/' + termID + '/resize',
665       { cols: cols, rows: rows });
666   }
667
668   /**
669   ** Private functions
670   ***/
671
672   private _RegisterEvents() {
673     // Register to all existing events
674     this._post('/events/register', { 'name': 'event:all' })
675       .subscribe(
676         res => { },
677         error => {
678           this.alert.error('ERROR while registering to all events: ' + error);
679         },
680     );
681   }
682
683   private _getServer(ID: string): IXDServerCfg {
684     const svr = this._config.servers.filter(item => item.id === ID);
685     if (svr.length < 1) {
686       return null;
687     }
688     return svr[0];
689   }
690
691   private _getServerUrl(serverID: string): string | ErrorObservable {
692     const svr = this._getServer(serverID);
693     if (!svr || !svr.connected) {
694       if (isDevMode) {
695         console.log('ERROR: XDS Server unknown: serverID=' + serverID);
696       }
697       return Observable.throw('Cannot identify XDS Server');
698     }
699     return svr.partialUrl;
700   }
701
702   private _attachAuthHeaders(options?: any) {
703     options = options || {};
704     const headers = options.headers || new HttpHeaders();
705     // headers.append('Authorization', 'Basic ' + btoa('username:password'));
706     headers.append('Accept', 'application/json');
707     headers.append('Content-Type', 'application/json');
708     // headers.append('Access-Control-Allow-Origin', '*');
709
710     options.headers = headers;
711     return options;
712   }
713
714   private _get(url: string): Observable<any> {
715     return this.http.get(this.baseUrl + url, this._attachAuthHeaders())
716       .catch(this._decodeError);
717   }
718   private _post(url: string, body: any): Observable<any> {
719     return this.http.post(this.baseUrl + url, JSON.stringify(body), this._attachAuthHeaders())
720       .catch((error) => {
721         return this._decodeError(error);
722       });
723   }
724   private _put(url: string, body: any): Observable<any> {
725     return this.http.put(this.baseUrl + url, JSON.stringify(body), this._attachAuthHeaders())
726       .catch((error) => {
727         return this._decodeError(error);
728       });
729   }
730   private _delete(url: string): Observable<any> {
731     return this.http.delete(this.baseUrl + url, this._attachAuthHeaders())
732       .catch(this._decodeError);
733   }
734
735   private _decodeError(err: any) {
736     let e: string;
737     if (err instanceof HttpErrorResponse) {
738       e = (err.error && err.error.error) ? err.error.error : err.message || 'Unknown error';
739     } else if (typeof err === 'object') {
740       if (err.statusText) {
741         e = err.statusText;
742       } else if (err.error) {
743         e = String(err.error);
744       } else {
745         e = JSON.stringify(err);
746       }
747     } else {
748       e = err.message ? err.message : err.toString();
749     }
750     /* tslint:disable:no-console */
751     if (isDevMode) {
752       console.log('xdsagent.service - ERROR: ', e);
753     }
754     return Observable.throw(e);
755   }
756 }