diff options
Diffstat (limited to 'webapp/src/app/@core-xds/services')
11 files changed, 1019 insertions, 0 deletions
diff --git a/webapp/src/app/@core-xds/services/@core-xds-services.module.ts b/webapp/src/app/@core-xds/services/@core-xds-services.module.ts new file mode 100644 index 0000000..13714e1 --- /dev/null +++ b/webapp/src/app/@core-xds/services/@core-xds-services.module.ts @@ -0,0 +1,39 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AlertService } from './alert.service'; +import { ConfigService } from './config.service'; +import { ProjectService } from './project.service'; +import { SdkService } from './sdk.service'; +import { UserService } from './users.service'; +import { XDSConfigService } from './xds-config.service'; +import { XDSAgentService } from './xdsagent.service'; + +const SERVICES = [ + AlertService, + ConfigService, + ProjectService, + SdkService, + UserService, + XDSConfigService, + XDSAgentService, +]; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [ + ...SERVICES, + ], +}) +export class XdsServicesModule { + static forRoot(): ModuleWithProviders { + return <ModuleWithProviders>{ + ngModule: XdsServicesModule, + providers: [ + ...SERVICES, + ], + }; + } +} diff --git a/webapp/src/app/@core-xds/services/alert.service.spec.ts b/webapp/src/app/@core-xds/services/alert.service.spec.ts new file mode 100644 index 0000000..b3d364c --- /dev/null +++ b/webapp/src/app/@core-xds/services/alert.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AlertService } from './alert.service'; + +describe('AlertService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AlertService] + }); + }); + + it('should be created', inject([AlertService], (service: AlertService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/webapp/src/app/@core-xds/services/alert.service.ts b/webapp/src/app/@core-xds/services/alert.service.ts new file mode 100644 index 0000000..c15e176 --- /dev/null +++ b/webapp/src/app/@core-xds/services/alert.service.ts @@ -0,0 +1,73 @@ +import { Injectable, SecurityContext } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; + + +export type AlertType = 'error' | 'warning' | 'info' | 'success'; + +export interface IAlert { + type: AlertType; + msg: string; + show?: boolean; + dismissible?: boolean; + dismissTimeout?: number; // close alert after this time (in seconds) + id?: number; +} + +@Injectable() +export class AlertService { + public alerts: Observable<IAlert[]>; + + private _alerts: IAlert[]; + private alertsSubject = <Subject<IAlert[]>>new Subject(); + private uid = 0; + private defaultDismissTmo = 5; // in seconds + + constructor() { + this.alerts = this.alertsSubject.asObservable(); + this._alerts = []; + this.uid = 0; + } + + public error(msg: string, dismissTime?: number) { + this.add({ + type: 'error', msg: msg, dismissible: true, dismissTimeout: dismissTime + }); + } + + public warning(msg: string, dismissible?: boolean) { + this.add({ type: 'warning', msg: msg, dismissible: true, dismissTimeout: (dismissible ? this.defaultDismissTmo : 0) }); + } + + public info(msg: string) { + this.add({ type: 'info', msg: msg, dismissible: true, dismissTimeout: this.defaultDismissTmo }); + } + + public add(al: IAlert) { + const msg = String(al.msg).replace('\n', '<br>'); + // this._alerts.push({ + this._alerts = [{ + show: true, + type: al.type, + msg: msg, + dismissible: al.dismissible || true, + dismissTimeout: (al.dismissTimeout * 1000) || 0, + id: this.uid, + }]; + this.uid += 1; + this.alertsSubject.next(this._alerts); + + } + + public del(al: IAlert) { + /* + const idx = this._alerts.findIndex((a) => a.id === al.id); + if (idx > -1) { + this._alerts.splice(idx, 1); + this.alertsSubject.next(this._alerts); + } + */ + this._alerts = []; + this.alertsSubject.next(this._alerts); + } +} diff --git a/webapp/src/app/@core-xds/services/config.service.spec.ts b/webapp/src/app/@core-xds/services/config.service.spec.ts new file mode 100644 index 0000000..a20d4ba --- /dev/null +++ b/webapp/src/app/@core-xds/services/config.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ConfigService } from './config.service'; + +describe('ConfigService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ConfigService] + }); + }); + + it('should be created', inject([ConfigService], (service: ConfigService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/webapp/src/app/@core-xds/services/config.service.ts b/webapp/src/app/@core-xds/services/config.service.ts new file mode 100644 index 0000000..1ba9f2d --- /dev/null +++ b/webapp/src/app/@core-xds/services/config.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from 'ngx-cookie'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +import { AlertService, IAlert } from '../services/alert.service'; + +export interface IConfig { + language: string; + projectsRootDir: string; +} + +@Injectable() +export class ConfigService { + + public Conf$: Observable<IConfig>; + + private confSubject: BehaviorSubject<IConfig>; + private confStore: IConfig; + + constructor( + private cookie: CookieService, + private alert: AlertService, + ) { + this.load(); + this.confSubject = <BehaviorSubject<IConfig>>new BehaviorSubject(this.confStore); + this.Conf$ = this.confSubject.asObservable(); + } + + // Load config + load() { + // Try to retrieve previous config from cookie + const cookConf = this.cookie.getObject('xds-config'); + if (cookConf != null) { + this.confStore = <IConfig>cookConf; + } else { + // Set default config + this.confStore = { + language: 'ENG', + projectsRootDir: '', + // projects: [] + }; + } + } + + // Save config into cookie + save() { + // Notify subscribers + this.confSubject.next(Object.assign({}, this.confStore)); + + // Don't save projects in cookies (too big!) + const cfg = Object.assign({}, this.confStore); + this.cookie.putObject('xds-config', cfg); + } + + set projectsRootDir(p: string) { + this.confStore.projectsRootDir = p; + this.save(); + } + +} diff --git a/webapp/src/app/@core-xds/services/project.service.spec.ts b/webapp/src/app/@core-xds/services/project.service.spec.ts new file mode 100644 index 0000000..b8edfc7 --- /dev/null +++ b/webapp/src/app/@core-xds/services/project.service.spec.ts @@ -0,0 +1,17 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ProjectService } from './project.service'; + +describe('ProjectService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ProjectService] + }); + }); + + it('should be created', inject([ProjectService], (service: ProjectService) => { + expect(service).toBeTruthy(); + })); + + // FIXME SEB - add more tests, see https://angular.io/guide/http#mocking-philosophy +}); diff --git a/webapp/src/app/@core-xds/services/project.service.ts b/webapp/src/app/@core-xds/services/project.service.ts new file mode 100644 index 0000000..8aeed80 --- /dev/null +++ b/webapp/src/app/@core-xds/services/project.service.ts @@ -0,0 +1,207 @@ +import { Injectable, SecurityContext } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +import { XDSAgentService, IXDSProjectConfig } from '../services/xdsagent.service'; + +/* FIXME: syntax only compatible with TS>2.4.0 +export enum ProjectType { + UNSET = '', + NATIVE_PATHMAP = 'PathMap', + SYNCTHING = 'CloudSync' +} +*/ +export type ProjectTypeEnum = '' | 'PathMap' | 'CloudSync'; +export const ProjectType = { + UNSET: '', + NATIVE_PATHMAP: 'PathMap', + SYNCTHING: 'CloudSync' +}; + +export const ProjectTypes = [ + { value: ProjectType.NATIVE_PATHMAP, display: 'Path mapping' }, + { value: ProjectType.SYNCTHING, display: 'Cloud Sync' } +]; + +export const ProjectStatus = { + ErrorConfig: 'ErrorConfig', + Disable: 'Disable', + Enable: 'Enable', + Pause: 'Pause', + Syncing: 'Syncing' +}; + +export interface IProject { + id?: string; + serverId: string; + label: string; + pathClient: string; + pathServer?: string; + type: ProjectTypeEnum; + status?: string; + isInSync?: boolean; + isUsable?: boolean; + serverPrjDef?: IXDSProjectConfig; + isExpanded?: boolean; + visible?: boolean; + defaultSdkID?: string; +} + +@Injectable() +export class ProjectService { + public Projects$: Observable<IProject[]>; + + private _prjsList: IProject[] = []; + private current: IProject; + private prjsSubject = <BehaviorSubject<IProject[]>>new BehaviorSubject(this._prjsList); + + constructor(private xdsSvr: XDSAgentService) { + this.current = null; + this.Projects$ = this.prjsSubject.asObservable(); + + this.xdsSvr.getProjects().subscribe((projects) => { + this._prjsList = []; + projects.forEach(p => { + this._addProject(p, true); + }); + this.prjsSubject.next(Object.assign([], this._prjsList)); + }); + + // Update Project data + this.xdsSvr.ProjectState$.subscribe(prj => { + const i = this._getProjectIdx(prj.id); + if (i >= 0) { + // XXX for now, only isInSync and status may change + this._prjsList[i].isInSync = prj.isInSync; + this._prjsList[i].status = prj.status; + this._prjsList[i].isUsable = this._isUsableProject(prj); + this.prjsSubject.next(Object.assign([], this._prjsList)); + } + }); + + // Add listener on create and delete project events + this.xdsSvr.addEventListener('event:project-add', (ev) => { + if (ev && ev.data && ev.data.id) { + this._addProject(ev.data); + } else { + console.log('Warning: received events with unknown data: ev=', ev); + } + }); + this.xdsSvr.addEventListener('event:project-delete', (ev) => { + if (ev && ev.data && ev.data.id) { + const idx = this._prjsList.findIndex(item => item.id === ev.data.id); + if (idx === -1) { + console.log('Warning: received events on unknown project id: ev=', ev); + return; + } + this._prjsList.splice(idx, 1); + this.prjsSubject.next(Object.assign([], this._prjsList)); + } else { + console.log('Warning: received events with unknown data: ev=', ev); + } + }); + + } + + public setCurrent(s: IProject) { + this.current = s; + } + + public getCurrent(): IProject { + return this.current; + } + + public getCurrentId(): string { + if (this.current && this.current.id) { + return this.current.id; + } + return ''; + } + + Add(prj: IProject): Observable<IProject> { + const xdsPrj: IXDSProjectConfig = { + id: '', + serverId: prj.serverId, + label: prj.label || '', + clientPath: prj.pathClient.trim(), + serverPath: prj.pathServer, + type: prj.type, + defaultSdkID: prj.defaultSdkID, + }; + // Send config to XDS server + return this.xdsSvr.addProject(xdsPrj) + .map(xp => this._convToIProject(xp)); + } + + Delete(prj: IProject): Observable<IProject> { + const idx = this._getProjectIdx(prj.id); + const delPrj = prj; + if (idx === -1) { + throw new Error('Invalid project id (id=' + prj.id + ')'); + } + return this.xdsSvr.deleteProject(prj.id) + .map(res => delPrj); + } + + Sync(prj: IProject): Observable<string> { + const idx = this._getProjectIdx(prj.id); + if (idx === -1) { + throw new Error('Invalid project id (id=' + prj.id + ')'); + } + return this.xdsSvr.syncProject(prj.id); + } + + private _isUsableProject(p) { + return p && p.isInSync && + (p.status === ProjectStatus.Enable) && + (p.status !== ProjectStatus.Syncing); + } + + private _getProjectIdx(id: string): number { + return this._prjsList.findIndex((item) => item.id === id); + } + + private _convToIProject(rPrj: IXDSProjectConfig): IProject { + // Convert XDSFolderConfig to IProject + const pp: IProject = { + id: rPrj.id, + serverId: rPrj.serverId, + label: rPrj.label, + pathClient: rPrj.clientPath, + pathServer: rPrj.serverPath, + type: rPrj.type, + status: rPrj.status, + isInSync: rPrj.isInSync, + isUsable: this._isUsableProject(rPrj), + defaultSdkID: rPrj.defaultSdkID, + serverPrjDef: Object.assign({}, rPrj), // do a copy + }; + return pp; + } + + private _addProject(rPrj: IXDSProjectConfig, noNext?: boolean): IProject { + + // Convert XDSFolderConfig to IProject + const pp = this._convToIProject(rPrj); + + // add new project + this._prjsList.push(pp); + + // sort project array + this._prjsList.sort((a, b) => { + if (a.label < b.label) { + return -1; + } + if (a.label > b.label) { + return 1; + } + return 0; + }); + + if (!noNext) { + this.prjsSubject.next(Object.assign([], this._prjsList)); + } + + return pp; + } +} diff --git a/webapp/src/app/@core-xds/services/sdk.service.ts b/webapp/src/app/@core-xds/services/sdk.service.ts new file mode 100644 index 0000000..d1daa86 --- /dev/null +++ b/webapp/src/app/@core-xds/services/sdk.service.ts @@ -0,0 +1,66 @@ +import { Injectable, SecurityContext } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +import { XDSAgentService } from '../services/xdsagent.service'; + +import 'rxjs/add/observable/throw'; + +export interface ISdk { + id: string; + profile: string; + version: string; + arch: number; + path: string; +} + +@Injectable() +export class SdkService { + public Sdks$: Observable<ISdk[]>; + + private _sdksList = []; + private current: ISdk; + private sdksSubject = <BehaviorSubject<ISdk[]>>new BehaviorSubject(this._sdksList); + + constructor(private xdsSvr: XDSAgentService) { + this.current = null; + this.Sdks$ = this.sdksSubject.asObservable(); + + this.xdsSvr.XdsConfig$.subscribe(cfg => { + if (!cfg || cfg.servers.length < 1) { + return; + } + // FIXME support multiple server + // cfg.servers.forEach(svr => { + this.xdsSvr.getSdks(cfg.servers[0].id).subscribe((s) => { + this._sdksList = s; + this.sdksSubject.next(s); + }); + }); + } + + public setCurrent(s: ISdk) { + this.current = s; + } + + public getCurrent(): ISdk { + return this.current; + } + + public getCurrentId(): string { + if (this.current && this.current.id) { + return this.current.id; + } + return ''; + } + + public add(sdk: ISdk): Observable<ISdk> { + // TODO SEB + return Observable.throw('Not implement yet'); + } + + public delete(sdk: ISdk): Observable<ISdk> { + // TODO SEB + return Observable.throw('Not implement yet'); + } +} diff --git a/webapp/src/app/@core-xds/services/users.service.ts b/webapp/src/app/@core-xds/services/users.service.ts new file mode 100644 index 0000000..e187c10 --- /dev/null +++ b/webapp/src/app/@core-xds/services/users.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/of'; + +let counter = 0; + +@Injectable() +export class UserService { + + private users = { + anonymous: { name: 'Anonymous', picture: 'assets/images/anonymous.png' }, + }; + + private userArray: any[]; + + constructor() { + // this.userArray = Object.values(this.users); + } + + getUsers(): Observable<any> { + return Observable.of(this.users); + } + + getUserArray(): Observable<any[]> { + return Observable.of(this.userArray); + } + + getUser(): Observable<any> { + counter = (counter + 1) % this.userArray.length; + return Observable.of(this.userArray[counter]); + } +} diff --git a/webapp/src/app/@core-xds/services/xds-config.service.ts b/webapp/src/app/@core-xds/services/xds-config.service.ts new file mode 100644 index 0000000..7559673 --- /dev/null +++ b/webapp/src/app/@core-xds/services/xds-config.service.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +import { AlertService, IAlert } from '../services/alert.service'; +import { XDSAgentService, IAgentStatus, IXDServerCfg } from '../../@core-xds/services/xdsagent.service'; + +import 'rxjs/add/operator/publish'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/catch'; + + +@Injectable() +export class XDSConfigService { + + // Conf$: Observable<IXdsConfig>; + xdsServers: IXDServerCfg[]; + + // private confSubject: BehaviorSubject<IXdsConfig>; + // private confStore: IXdsConfig; + + private _curServer: IXDServerCfg = { id: '', url: '', connRetry: 0, connected: false }; + private curServer$ = new Subject<IXDServerCfg>(); + + constructor( + private alert: AlertService, + private xdsAgentSvr: XDSAgentService, + ) { + /* + this.confSubject = <BehaviorSubject<IXdsConfig>>new BehaviorSubject(this.confStore); + this.Conf$ = this.confSubject.asObservable(); + */ + + // Update servers list + this.xdsAgentSvr.XdsConfig$.subscribe(cfg => { + if (!cfg || cfg.servers.length < 1) { + return; + } + this.xdsServers = cfg.servers; + this._updateCurServer(); + }); + } + + onCurServer(): Observable<IXDServerCfg> { + return this.curServer$.publish().refCount(); + } + + getCurServer(): IXDServerCfg { + return Object.assign({}, this._curServer); + } + + setCurServer(svr: IXDServerCfg): Observable<IXDServerCfg> { + const curSvr = this._getCurServer(); + + if (!curSvr.connected || curSvr.url !== svr.url) { + return this.xdsAgentSvr.setServerUrl(curSvr.id, svr.url, svr.connRetry) + .map(cfg => this._updateCurServer()) + .catch(err => { + this._curServer.connected = false; + this.curServer$.next(Object.assign({}, this._curServer)); + return Observable.throw(err); + }); + } else { + if (curSvr.connRetry !== svr.connRetry) { + return this.xdsAgentSvr.setServerRetry(curSvr.id, svr.connRetry) + .map(cfg => this._updateCurServer()) + .catch(err => { + this.curServer$.next(Object.assign({}, this._curServer)); + return Observable.throw(err); + }); + } + } + return Observable.of(curSvr); + } + + private _updateCurServer() { + this._curServer = this._getCurServer(); + this.curServer$.next(Object.assign({}, this._curServer)); + } + + private _getCurServer(url?: string): IXDServerCfg { + if (!this.xdsServers) { + return this._curServer; + } + + // Init the 1st time + let svrUrl = url || this._curServer.url; + if (this._curServer.url === '' && this.xdsServers.length > 0) { + svrUrl = this.xdsServers[0].url; + } + + const svr = this.xdsServers.filter(s => s.url === svrUrl); + return svr[0]; + } + +} diff --git a/webapp/src/app/@core-xds/services/xdsagent.service.ts b/webapp/src/app/@core-xds/services/xdsagent.service.ts new file mode 100644 index 0000000..56e493f --- /dev/null +++ b/webapp/src/app/@core-xds/services/xdsagent.service.ts @@ -0,0 +1,397 @@ +import { Injectable, Inject } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; +import { DOCUMENT } from '@angular/common'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import * as io from 'socket.io-client'; + +import { AlertService } from './alert.service'; +import { ISdk } from './sdk.service'; +import { ProjectType, ProjectTypeEnum } from './project.service'; + +// Import RxJs required methods +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/observable/throw'; +import 'rxjs/add/operator/mergeMap'; +import 'rxjs/add/observable/of'; +import 'rxjs/add/operator/retryWhen'; + + +export interface IXDSConfigProject { + id: string; + path: string; + clientSyncThingID: string; + type: string; + label?: string; + defaultSdkID?: string; +} + +interface IXDSBuilderConfig { + ip: string; + port: string; + syncThingID: string; +} + +export interface IXDSProjectConfig { + id: string; + serverId: string; + label: string; + clientPath: string; + serverPath?: string; + type: ProjectTypeEnum; + status?: string; + isInSync?: boolean; + defaultSdkID: string; +} + +export interface IXDSVer { + id: string; + version: string; + apiVersion: string; + gitTag: string; +} + +export interface IXDSVersions { + client: IXDSVer; + servers: IXDSVer[]; +} + +export interface IXDServerCfg { + id: string; + url: string; + apiUrl?: string; + partialUrl?: string; + connRetry: number; + connected: boolean; +} + +export interface IXDSConfig { + servers: IXDServerCfg[]; +} + +export interface ISdkMessage { + wsID: string; + msgType: string; + data: any; +} + +export interface ICmdOutput { + cmdID: string; + timestamp: string; + stdout: string; + stderr: string; +} + +export interface ICmdExit { + cmdID: string; + timestamp: string; + code: number; + error: string; +} + +export interface IServerStatus { + id: string; + connected: boolean; +} + +export interface IAgentStatus { + connected: boolean; + servers: IServerStatus[]; +} + + +@Injectable() +export class XDSAgentService { + + public XdsConfig$: Observable<IXDSConfig>; + public Status$: Observable<IAgentStatus>; + public ProjectState$ = <Subject<IXDSProjectConfig>>new Subject(); + public CmdOutput$ = <Subject<ICmdOutput>>new Subject(); + public CmdExit$ = <Subject<ICmdExit>>new Subject(); + + private baseUrl: string; + private wsUrl: string; + private _config = <IXDSConfig>{ servers: [] }; + private _status = { connected: false, servers: [] }; + + private configSubject = <BehaviorSubject<IXDSConfig>>new BehaviorSubject(this._config); + private statusSubject = <BehaviorSubject<IAgentStatus>>new BehaviorSubject(this._status); + + private socket: SocketIOClient.Socket; + + constructor( @Inject(DOCUMENT) private document: Document, + private http: HttpClient, private alert: AlertService) { + + this.XdsConfig$ = this.configSubject.asObservable(); + this.Status$ = this.statusSubject.asObservable(); + + const originUrl = this.document.location.origin; + this.baseUrl = originUrl + '/api/v1'; + + const re = originUrl.match(/http[s]?:\/\/([^\/]*)[\/]?/); + if (re === null || re.length < 2) { + console.error('ERROR: cannot determine Websocket url'); + } else { + this.wsUrl = 'ws://' + re[1]; + this._handleIoSocket(); + this._RegisterEvents(); + } + } + + private _NotifyXdsAgentState(sts: boolean) { + this._status.connected = sts; + this.statusSubject.next(Object.assign({}, this._status)); + + // Update XDS config including XDS Server list when connected + if (sts) { + this.getConfig().subscribe(c => { + this._config = c; + this._NotifyXdsServerState(); + this.configSubject.next(Object.assign({ servers: [] }, this._config)); + }); + } + } + + private _NotifyXdsServerState() { + this._status.servers = this._config.servers.map(svr => { + return { id: svr.id, connected: svr.connected }; + }); + this.statusSubject.next(Object.assign({}, this._status)); + } + + private _handleIoSocket() { + this.socket = io(this.wsUrl, { transports: ['websocket'] }); + + this.socket.on('connect_error', (res) => { + this._NotifyXdsAgentState(false); + console.error('XDS Agent WebSocket Connection error !'); + }); + + this.socket.on('connect', (res) => { + this._NotifyXdsAgentState(true); + }); + + this.socket.on('disconnection', (res) => { + this._NotifyXdsAgentState(false); + this.alert.error('WS disconnection: ' + res); + }); + + this.socket.on('error', (err) => { + console.error('WS error:', err); + }); + + this.socket.on('make:output', data => { + this.CmdOutput$.next(Object.assign({}, <ICmdOutput>data)); + }); + + this.socket.on('make:exit', data => { + this.CmdExit$.next(Object.assign({}, <ICmdExit>data)); + }); + + this.socket.on('exec:output', data => { + this.CmdOutput$.next(Object.assign({}, <ICmdOutput>data)); + }); + + this.socket.on('exec:exit', data => { + this.CmdExit$.next(Object.assign({}, <ICmdExit>data)); + }); + + // Events + // (project-add and project-delete events are managed by project.service) + this.socket.on('event:server-config', ev => { + if (ev && ev.data) { + const cfg: IXDServerCfg = ev.data; + const idx = this._config.servers.findIndex(el => el.id === cfg.id); + if (idx >= 0) { + this._config.servers[idx] = Object.assign({}, cfg); + this._NotifyXdsServerState(); + } + this.configSubject.next(Object.assign({}, this._config)); + } + }); + + this.socket.on('event:project-state-change', ev => { + if (ev && ev.data) { + this.ProjectState$.next(Object.assign({}, ev.data)); + } + }); + + } + + /** + ** Events + ***/ + addEventListener(ev: string, fn: Function): SocketIOClient.Emitter { + return this.socket.addEventListener(ev, fn); + } + + /** + ** Misc / Version + ***/ + getVersion(): Observable<IXDSVersions> { + return this._get('/version'); + } + + /*** + ** Config + ***/ + getConfig(): Observable<IXDSConfig> { + return this._get('/config'); + } + + setConfig(cfg: IXDSConfig): Observable<IXDSConfig> { + return this._post('/config', cfg); + } + + setServerRetry(serverID: string, retry: number): Observable<IXDSConfig> { + const svr = this._getServer(serverID); + if (!svr) { + return Observable.throw('Unknown server ID'); + } + if (retry < 0 || Number.isNaN(retry) || retry == null) { + return Observable.throw('Not a valid number'); + } + svr.connRetry = retry; + return this._setConfig(); + } + + setServerUrl(serverID: string, url: string, retry: number): Observable<IXDSConfig> { + const svr = this._getServer(serverID); + if (!svr) { + return Observable.throw('Unknown server ID'); + } + svr.connected = false; + svr.url = url; + if (!Number.isNaN(retry) && retry > 0) { + svr.connRetry = retry; + } + this._NotifyXdsServerState(); + return this._setConfig(); + } + + private _setConfig(): Observable<IXDSConfig> { + return this.setConfig(this._config) + .map(newCfg => { + this._config = newCfg; + this.configSubject.next(Object.assign({}, this._config)); + return this._config; + }); + } + + /*** + ** SDKs + ***/ + getSdks(serverID: string): Observable<ISdk[]> { + const svr = this._getServer(serverID); + if (!svr || !svr.connected) { + return Observable.of([]); + } + + return this._get(svr.partialUrl + '/sdks'); + } + + /*** + ** Projects + ***/ + getProjects(): Observable<IXDSProjectConfig[]> { + return this._get('/projects'); + } + + addProject(cfg: IXDSProjectConfig): Observable<IXDSProjectConfig> { + return this._post('/projects', cfg); + } + + deleteProject(id: string): Observable<IXDSProjectConfig> { + return this._delete('/projects/' + id); + } + + syncProject(id: string): Observable<string> { + return this._post('/projects/sync/' + id, {}); + } + + /*** + ** Exec + ***/ + exec(prjID: string, dir: string, cmd: string, sdkid?: string, args?: string[], env?: string[]): Observable<any> { + return this._post('/exec', + { + id: prjID, + rpath: dir, + cmd: cmd, + sdkID: sdkid || '', + args: args || [], + env: env || [], + }); + } + + /** + ** Private functions + ***/ + + private _RegisterEvents() { + // Register to all existing events + this._post('/events/register', { 'name': 'event:all' }) + .subscribe( + res => { }, + error => { + this.alert.error('ERROR while registering to all events: ' + error); + } + ); + } + + private _getServer(ID: string): IXDServerCfg { + const svr = this._config.servers.filter(item => item.id === ID); + if (svr.length < 1) { + return null; + } + return svr[0]; + } + + private _attachAuthHeaders(options?: any) { + options = options || {}; + const headers = options.headers || new HttpHeaders(); + // headers.append('Authorization', 'Basic ' + btoa('username:password')); + headers.append('Accept', 'application/json'); + headers.append('Content-Type', 'application/json'); + // headers.append('Access-Control-Allow-Origin', '*'); + + options.headers = headers; + return options; + } + + private _get(url: string): Observable<any> { + return this.http.get(this.baseUrl + url, this._attachAuthHeaders()) + .catch(this._decodeError); + } + private _post(url: string, body: any): Observable<any> { + return this.http.post(this.baseUrl + url, JSON.stringify(body), this._attachAuthHeaders()) + .catch((error) => { + return this._decodeError(error); + }); + } + private _delete(url: string): Observable<any> { + return this.http.delete(this.baseUrl + url, this._attachAuthHeaders()) + .catch(this._decodeError); + } + + private _decodeError(err: any) { + let e: string; + if (err instanceof HttpErrorResponse) { + e = (err.error && err.error.error) ? err.error.error : err.message || 'Unknown error'; + } else if (typeof err === 'object') { + if (err.statusText) { + e = err.statusText; + } else if (err.error) { + e = String(err.error); + } else { + e = JSON.stringify(err); + } + } else { + e = err.message ? err.message : err.toString(); + } + console.log('xdsagent.service - ERROR: ', e); + return Observable.throw(e); + } +} |