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';
10 import { ISdk } from './sdk.service';
13 // Import RxJs required methods
14 import 'rxjs/add/operator/map';
15 import 'rxjs/add/operator/catch';
16 import 'rxjs/add/observable/throw';
17 import 'rxjs/add/operator/mergeMap';
20 export interface IXDSConfigProject {
23 clientSyncThingID: string;
26 defaultSdkID?: string;
29 interface IXDSBuilderConfig {
35 export interface IXDSFolderConfig {
43 // FIXME better with union but tech pb with go code
44 //data?: IXDSPathMapConfig|IXDSCloudSyncConfig;
45 dataPathMap?:IXDSPathMapConfig;
46 dataCloudSync?:IXDSCloudSyncConfig;
49 export interface IXDSPathMapConfig {
54 export interface IXDSCloudSyncConfig {
56 builderSThgID?: string;
59 interface IXDSConfig {
61 builder: IXDSBuilderConfig;
62 folders: IXDSFolderConfig[];
65 export interface IXDSAgentTarball {
73 export interface IXDSAgentInfo {
74 tarballs: IXDSAgentTarball[];
77 export interface ISdkMessage {
83 export interface ICmdOutput {
90 export interface ICmdExit {
97 export interface IServerStatus {
98 WS_connected: boolean;
102 const FOLDER_TYPE_CLOUDSYNC = 2;
105 export class XDSServerService {
107 public CmdOutput$ = <Subject<ICmdOutput>>new Subject();
108 public CmdExit$ = <Subject<ICmdExit>>new Subject();
109 public Status$: Observable<IServerStatus>;
111 private baseUrl: string;
112 private wsUrl: string;
113 private _status = { WS_connected: false };
114 private statusSubject = <BehaviorSubject<IServerStatus>>new BehaviorSubject(this._status);
117 private socket: SocketIOClient.Socket;
119 constructor(private http: Http, private _window: Window, private alert: AlertService) {
121 this.Status$ = this.statusSubject.asObservable();
123 this.baseUrl = this._window.location.origin + '/api/v1';
124 let re = this._window.location.origin.match(/http[s]?:\/\/([^\/]*)[\/]?/);
125 if (re === null || re.length < 2) {
126 console.error('ERROR: cannot determine Websocket url');
128 this.wsUrl = 'ws://' + re[1];
129 this._handleIoSocket();
133 private _WSState(sts: boolean) {
134 this._status.WS_connected = sts;
135 this.statusSubject.next(Object.assign({}, this._status));
138 private _handleIoSocket() {
139 this.socket = io(this.wsUrl, { transports: ['websocket'] });
141 this.socket.on('connect_error', (res) => {
142 this._WSState(false);
143 console.error('WS Connect_error ', res);
146 this.socket.on('connect', (res) => {
150 this.socket.on('disconnection', (res) => {
151 this._WSState(false);
152 this.alert.error('WS disconnection: ' + res);
155 this.socket.on('error', (err) => {
156 console.error('WS error:', err);
159 this.socket.on('make:output', data => {
160 this.CmdOutput$.next(Object.assign({}, <ICmdOutput>data));
163 this.socket.on('make:exit', data => {
164 this.CmdExit$.next(Object.assign({}, <ICmdExit>data));
167 this.socket.on('exec:output', data => {
168 this.CmdOutput$.next(Object.assign({}, <ICmdOutput>data));
171 this.socket.on('exec:exit', data => {
172 this.CmdExit$.next(Object.assign({}, <ICmdExit>data));
177 getSdks(): Observable<ISdk[]> {
178 return this._get('/sdks');
181 getXdsAgentInfo(): Observable<IXDSAgentInfo> {
182 return this._get('/xdsagent/info');
185 getProjects(): Observable<IXDSFolderConfig[]> {
186 return this._get('/folders');
189 addProject(cfg: IXDSFolderConfig): Observable<IXDSFolderConfig> {
190 return this._post('/folder', cfg);
193 deleteProject(id: string): Observable<IXDSFolderConfig> {
194 return this._delete('/folder/' + id);
197 exec(prjID: string, dir: string, cmd: string, sdkid?: string, args?: string[], env?: string[]): Observable<any> {
198 return this._post('/exec',
209 make(prjID: string, dir: string, sdkid?: string, args?: string[], env?: string[]): Observable<any> {
210 return this._post('/make',
221 private _attachAuthHeaders(options?: any) {
222 options = options || {};
223 let headers = options.headers || new Headers();
224 // headers.append('Authorization', 'Basic ' + btoa('username:password'));
225 headers.append('Accept', 'application/json');
226 headers.append('Content-Type', 'application/json');
227 // headers.append('Access-Control-Allow-Origin', '*');
229 options.headers = headers;
233 private _get(url: string): Observable<any> {
234 return this.http.get(this.baseUrl + url, this._attachAuthHeaders())
235 .map((res: Response) => res.json())
236 .catch(this._decodeError);
238 private _post(url: string, body: any): Observable<any> {
239 return this.http.post(this.baseUrl + url, JSON.stringify(body), this._attachAuthHeaders())
240 .map((res: Response) => res.json())
242 return this._decodeError(error);
245 private _delete(url: string): Observable<any> {
246 return this.http.delete(this.baseUrl + url, this._attachAuthHeaders())
247 .map((res: Response) => res.json())
248 .catch(this._decodeError);
251 private _decodeError(err: any) {
253 if (err instanceof Response) {
254 const body = err.json() || 'Server error';
255 e = body.error || JSON.stringify(body);
256 if (!e || e === "") {
257 e = `${err.status} - ${err.statusText || 'Unknown error'}`;
259 } else if (typeof err === "object") {
260 if (err.statusText) {
262 } else if (err.error) {
263 e = String(err.error);
265 e = JSON.stringify(err);
268 e = err.message ? err.message : err.toString();
270 return Observable.throw(e);