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