94469fe35abd8f34c3534d5f3b1f6b4f897ac473
[src/xds/xds-agent.git] / webapp / src / app / @core-xds / services / project.service.ts
1 import { Injectable, SecurityContext, isDevMode } from '@angular/core';
2 import { Observable } from 'rxjs/Observable';
3 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
4
5 import { XDSAgentService, IXDSProjectConfig } from '../services/xdsagent.service';
6
7 /* FIXME: syntax only compatible with TS>2.4.0
8 export enum ProjectType {
9     UNSET = '',
10     NATIVE_PATHMAP = 'PathMap',
11     SYNCTHING = 'CloudSync'
12 }
13 */
14 export type ProjectTypeEnum = '' | 'PathMap' | 'CloudSync';
15 export const ProjectType = {
16   UNSET: '',
17   NATIVE_PATHMAP: 'PathMap',
18   SYNCTHING: 'CloudSync',
19 };
20
21 export const ProjectTypes = [
22   { value: ProjectType.NATIVE_PATHMAP, display: 'Path mapping' },
23   { value: ProjectType.SYNCTHING, display: 'Cloud Sync' },
24 ];
25
26 export const ProjectStatus = {
27   ErrorConfig: 'ErrorConfig',
28   Disable: 'Disable',
29   Enable: 'Enable',
30   Pause: 'Pause',
31   Syncing: 'Syncing',
32 };
33
34 export interface IUISettings {
35   subpath: string;
36   cmdClean: string;
37   cmdPrebuild: string;
38   cmdBuild: string;
39   cmdPopulate: string;
40   cmdArgs: string[];
41   envVars: string[];
42 }
43 export interface IProject {
44   id?: string;
45   serverId: string;
46   label: string;
47   pathClient: string;
48   pathServer?: string;
49   type: ProjectTypeEnum;
50   status?: string;
51   isInSync?: boolean;
52   isUsable?: boolean;
53   serverPrjDef?: IXDSProjectConfig;
54   isExpanded?: boolean;
55   visible?: boolean;
56   defaultSdkID?: string;
57   uiSettings?: IUISettings;
58 }
59
60 const defaultUISettings: IUISettings = {
61   subpath: '',
62   cmdClean: 'rm -rf build && echo Done',
63   cmdPrebuild: 'mkdir -p build && cd build && cmake ..',
64   cmdBuild: 'cd build && make',
65   cmdPopulate: 'cd build && make remote-target-populate',
66   cmdArgs: [],
67   envVars: [],
68 };
69
70 @Injectable()
71 export class ProjectService {
72   projects$: Observable<IProject[]>;
73   curProject$: Observable<IProject>;
74
75   private _prjsList: IProject[] = [];
76   private prjsSubject = <BehaviorSubject<IProject[]>>new BehaviorSubject(this._prjsList);
77   private _current: IProject;
78   private curPrjSubject = <BehaviorSubject<IProject>>new BehaviorSubject(this._current);
79
80   constructor(private xdsSvr: XDSAgentService) {
81     this._current = null;
82     this.projects$ = this.prjsSubject.asObservable();
83     this.curProject$ = this.curPrjSubject.asObservable();
84
85     // Load initial projects list
86     this.xdsSvr.getProjects().subscribe((projects) => {
87       this._prjsList = [];
88       projects.forEach(p => {
89         this._addProject(p, true);
90       });
91
92       // TODO: get previous val from xds-config service / cookie
93       if (this._prjsList.length > 0) {
94         this._current = this._prjsList[0];
95         this.curPrjSubject.next(this._current);
96       }
97
98       this.prjsSubject.next(this._prjsList);
99     });
100
101     // Add listener on projects creation, deletion and change events
102     this.xdsSvr.onProjectAdd().subscribe(prj => this._addProject(prj));
103     this.xdsSvr.onProjectDelete().subscribe(prj => this._delProject(prj));
104     this.xdsSvr.onProjectChange().subscribe(prj => this._updateProject(prj));
105   }
106
107   setCurrent(p: IProject): IProject | undefined {
108     if (!p) {
109       this._current = null;
110       return undefined;
111     }
112     return this.setCurrentById(p.id);
113   }
114
115   setCurrentById(id: string): IProject | undefined {
116     const p = this._prjsList.find(item => item.id === id);
117     if (p) {
118       this._current = p;
119       this.curPrjSubject.next(this._current);
120     }
121     return this._current;
122   }
123
124   getCurrent(): IProject {
125     return this._current;
126   }
127
128   add(prj: IProject): Observable<IProject> {
129     // Send config to XDS server
130     return this.xdsSvr.addProject(this._convToIXdsProject(prj))
131       .map(xp => this._convToIProject(xp));
132   }
133
134   delete(prj: IProject): Observable<IProject> {
135     const idx = this._getProjectIdx(prj.id);
136     const delPrj = prj;
137     if (idx === -1) {
138       throw new Error('Invalid project id (id=' + prj.id + ')');
139     }
140     return this.xdsSvr.deleteProject(prj.id)
141       .map(res => delPrj);
142   }
143
144   sync(prj: IProject): Observable<string> {
145     const idx = this._getProjectIdx(prj.id);
146     if (idx === -1) {
147       throw new Error('Invalid project id (id=' + prj.id + ')');
148     }
149     return this.xdsSvr.syncProject(prj.id);
150   }
151
152   setSettings(prj: IProject): Observable<IProject> {
153     return this.xdsSvr.updateProject(this._convToIXdsProject(prj))
154       .map(xp => this._convToIProject(xp));
155   }
156
157   getDefaultSettings(): IUISettings {
158     return defaultUISettings;
159   }
160
161   /***  Private functions  ***/
162
163   private _isUsableProject(p) {
164     return p && p.isInSync &&
165       (p.status === ProjectStatus.Enable) &&
166       (p.status !== ProjectStatus.Syncing);
167   }
168
169   private _getProjectIdx(id: string): number {
170     return this._prjsList.findIndex((item) => item.id === id);
171   }
172
173
174   private _convToIXdsProject(prj: IProject): IXDSProjectConfig {
175     const xPrj: IXDSProjectConfig = {
176       id: prj.id || '',
177       serverId: prj.serverId,
178       label: prj.label || '',
179       clientPath: prj.pathClient.trim(),
180       serverPath: prj.pathServer,
181       type: prj.type,
182       defaultSdkID: prj.defaultSdkID,
183       clientData: JSON.stringify(prj.uiSettings || defaultUISettings),
184     };
185     return xPrj;
186   }
187
188   private _convToIProject(rPrj: IXDSProjectConfig): IProject {
189     let settings = defaultUISettings;
190     if (rPrj.clientData && rPrj.clientData !== '') {
191       settings = JSON.parse(rPrj.clientData);
192     }
193
194     // Convert XDSFolderConfig to IProject
195     const pp: IProject = {
196       id: rPrj.id,
197       serverId: rPrj.serverId,
198       label: rPrj.label,
199       pathClient: rPrj.clientPath,
200       pathServer: rPrj.serverPath,
201       type: rPrj.type,
202       status: rPrj.status,
203       isInSync: rPrj.isInSync,
204       isUsable: this._isUsableProject(rPrj),
205       defaultSdkID: rPrj.defaultSdkID,
206       serverPrjDef: Object.assign({}, rPrj),  // do a copy
207       uiSettings: settings,
208     };
209     return pp;
210   }
211
212   private _addProject(prj: IXDSProjectConfig, noNext?: boolean): IProject {
213
214     // Convert XDSFolderConfig to IProject
215     const pp = this._convToIProject(prj);
216
217     // add new project
218     this._prjsList.push(pp);
219
220     // sort project array
221     this._prjsList.sort((a, b) => {
222       if (a.label < b.label) {
223         return -1;
224       }
225       if (a.label > b.label) {
226         return 1;
227       }
228       return 0;
229     });
230
231     if (!noNext) {
232       this.prjsSubject.next(this._prjsList);
233     }
234
235     return pp;
236   }
237
238   private _delProject(prj: IXDSProjectConfig) {
239     const idx = this._prjsList.findIndex(item => item.id === prj.id);
240     if (idx === -1) {
241       if (isDevMode) {
242         /* tslint:disable:no-console */
243         console.log('Warning: Try to delete project unknown id: prj=', prj);
244       }
245       return;
246     }
247     const delId = this._prjsList[idx].id;
248     this._prjsList.splice(idx, 1);
249     if (this._prjsList[idx].id === this._current.id) {
250       this.setCurrent(this._prjsList[0]);
251     }
252     this.prjsSubject.next(this._prjsList);
253   }
254
255   private _updateProject(prj: IXDSProjectConfig) {
256     const i = this._getProjectIdx(prj.id);
257     if (i >= 0) {
258       // XXX for now, only isInSync and status may change
259       this._prjsList[i].isInSync = prj.isInSync;
260       this._prjsList[i].status = prj.status;
261       this._prjsList[i].isUsable = this._isUsableProject(prj);
262       this.prjsSubject.next(this._prjsList);
263     }
264   }
265
266 }