user.service.ts 10.2 KB
Newer Older
Lukas Burgey's avatar
Lukas Burgey committed
1
2
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
Lukas Burgey's avatar
Lukas Burgey committed
3
4
import {MatTableDataSource} from '@angular/material';

Lukas Burgey's avatar
Lukas Burgey committed
5
import {Observable} from 'rxjs/Observable';
6
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
7
import {Subject} from 'rxjs/Subject';
Lukas Burgey's avatar
Lukas Burgey committed
8
9

import {CookieService} from 'ngx-cookie-service';
10
11
import {StompConfig, StompRService} from '@stomp/ng2-stompjs';
import {Message} from '@stomp/stompjs';
Lukas Burgey's avatar
Lukas Burgey committed
12

Lukas Burgey's avatar
Lukas Burgey committed
13
import {SnackBarService} from './snackbar.service';
14
import {IdpService} from './idp.service';
Lukas Burgey's avatar
Lukas Burgey committed
15
import * as t from './types/types.module';
16
import { StateAPIResult as SAR } from './types/types.module';
Lukas Burgey's avatar
Lukas Burgey committed
17

Lukas Burgey's avatar
Lukas Burgey committed
18
19
20

@Injectable()
export class UserService {
21
22
  private initialized = false;
  private loggedIn: boolean = false;
Lukas Burgey's avatar
Lukas Burgey committed
23
  private loggedIn$ = new BehaviorSubject<boolean>(false);
Lukas Burgey's avatar
Lukas Burgey committed
24

25
26
  private sar: SAR;
  private sar$ = <BehaviorSubject<SAR>> new BehaviorSubject(new Object);
Lukas Burgey's avatar
Lukas Burgey committed
27
28

  private sshKeys: t.SSHKey[] = new Array<t.SSHKey>();
29
  private sshKeys$ = new BehaviorSubject<t.SSHKey[]>(this.sshKeys);
Lukas Burgey's avatar
Lukas Burgey committed
30

31
32
  private userState: t.UserState;
  private userState$ = <BehaviorSubject<t.UserState>> new BehaviorSubject(new Object);
Lukas Burgey's avatar
Lukas Burgey committed
33

34
  private messages: string[] = [];
Lukas Burgey's avatar
Lukas Burgey committed
35

36
37
38
  public groupSelector = (state: SAR) => state ? state.user ? state.user.groups : [] : [];
  public userSelector = (state: SAR) => state ? state.user : null;
  public serviceSelector = (state: SAR) => state ? state.services : [];
Lukas Burgey's avatar
Lukas Burgey committed
39
40

  constructor(
Lukas Burgey's avatar
Lukas Burgey committed
41
42
43
44
    private cookieService: CookieService,
    private http: HttpClient,
    private snackBar: SnackBarService,
    private idpService: IdpService,
45
    private _stompService: StompRService,
Lukas Burgey's avatar
Lukas Burgey committed
46
  ) {
47
    this.connect();
48
49
  }

50
51
52
53
  // PRIVATE API
  private disconnect(): void {
    this.loggedIn = false;
    this.sshKeys$.complete();
54

55
    this.loggedIn$.next(this.loggedIn);
Lukas Burgey's avatar
Lukas Burgey committed
56
57
  }

58
59
60
61
62
63
64
65
66
67
68
69
70
71
  private connect(): void {
    this.subscribeState().subscribe(
      (state: SAR) => {
        //console.log('sar$ yielded', state);

        // update users
        const user = this.userSelector(state);
        if (user) {
          this.connectLiveUpdates(user);
          this.sshKeys = user.ssh_keys;
        } else {
          this.sshKeys = [];
        }
        this.sshKeys$.next(this.sshKeys);
Lukas Burgey's avatar
Lukas Burgey committed
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
      },
      this.errorLogger,
      () => console.log('sar$ is complete'),
    );

    this.subscribeLoggedIn().subscribe(
      (loggedIn: boolean) => {

        if (loggedIn) {

          if (!this.initialized) {
            this.snackBar.open('Logged in');
          }
          this.initialized = true;
        }
        if (!loggedIn && this.initialized) {
          console.log('Logging out');
          this._stompService.disconnect();

          this.sar$.complete();
          this.userState$.complete();
          this.sshKeys$.complete();

          this.snackBar.open('Logged out');
        }
      },
      this.errorLogger,
      () => console.log('loggedIn$ completed'),
    );

    // start pulling updates
    this.fetch();
Lukas Burgey's avatar
Lukas Burgey committed
105
106
  }

107
  private connectLiveUpdates(user: t.User): void {
Lukas Burgey's avatar
Lukas Burgey committed
108
    // handle with care
109
    let login = user.id
Lukas Burgey's avatar
Lukas Burgey committed
110
    let passcode = this.cookieService.get('sessionid');
Lukas Burgey's avatar
Lukas Burgey committed
111

Lukas Burgey's avatar
Lukas Burgey committed
112
113
    const stompConfig: StompConfig = {
      // Which server?
114
      url: 'wss://'+window.location.host+'/ws',
115

Lukas Burgey's avatar
Lukas Burgey committed
116
117
118
119
120
121
      // Headers
      // Typical keys: login, passcode, host
      headers: {
        login: 'webpage-client:' + login,
        passcode: passcode,
      },
Lukas Burgey's avatar
Lukas Burgey committed
122

Lukas Burgey's avatar
Lukas Burgey committed
123
124
125
126
127
128
      // How often to heartbeat?
      // Interval in milliseconds, set to 0 to disable
      heartbeat_in: 0, // Typical value 0 - disabled
      heartbeat_out: 20000, // Typical value 20000 - every 20 seconds
      // Wait in milliseconds before attempting auto reconnect
      // Set to 0 to disable
129
130
      // Typical value 15000 (15 seconds)
      reconnect_delay: 15000,
131

Lukas Burgey's avatar
Lukas Burgey committed
132
133
134
135
136
      // Will log diagnostics on console
      debug: false,
    };
    this._stompService.config = stompConfig;
    this._stompService.initAndConnect();
Lukas Burgey's avatar
Lukas Burgey committed
137

Lukas Burgey's avatar
Lukas Burgey committed
138
    let subscription = this._stompService.subscribe(
139
      '/exchange/users/' + user.id.toString()
Lukas Burgey's avatar
Lukas Burgey committed
140
    );
141

Lukas Burgey's avatar
Lukas Burgey committed
142
143
144
145
146
147
    subscription.subscribe(
      (message: Message) => {
        let update : t.StateAPIResult = JSON.parse(message.body);
        if (update.error && update.error != '') {
          this.snackBar.open(update.error);
          this.messages.push(update.error);
Lukas Burgey's avatar
Lukas Burgey committed
148
        }
Lukas Burgey's avatar
Lukas Burgey committed
149
        if (update.user_state) {
150
          console.log('stomp received stateAPIUpdate', update.user_state);
Lukas Burgey's avatar
Lukas Burgey committed
151
          this.updateUserState(update.user_state);
Lukas Burgey's avatar
Lukas Burgey committed
152
        }
Lukas Burgey's avatar
Lukas Burgey committed
153
154
155
      },
      (error: any) => {
        console.log(error)
156
      }
Lukas Burgey's avatar
Lukas Burgey committed
157
    );
Lukas Burgey's avatar
Lukas Burgey committed
158
  }
159

160
  private updateUserState(newState: t.UserState): void {
Lukas Burgey's avatar
Lukas Burgey committed
161
      // did a login occur?
162
    let login = (!this.loggedIn && newState);
Lukas Burgey's avatar
Lukas Burgey committed
163
    // did a logout occur?
164
    let logout = (this.loggedIn && !newState);
Lukas Burgey's avatar
Lukas Burgey committed
165
166
167

    // -- Value updating --
    if (login) {
168
169
      this.loggedIn = true;
      this.loggedIn$.next(this.loggedIn);
170
    }
Lukas Burgey's avatar
Lukas Burgey committed
171
    if (logout) {
172
173
      this.loggedIn = false;
      this.loggedIn$.next(this.loggedIn);
174
      return
Lukas Burgey's avatar
Lukas Burgey committed
175
176
    }

177
178
179
180
    if (newState) {
      this.userState = newState;
      this.userState$.next(newState);
    }
Lukas Burgey's avatar
wip    
Lukas Burgey committed
181
182
  }

Lukas Burgey's avatar
Lukas Burgey committed
183
  private stateAPIUpdate(update: t.StateAPIResult) {
184
    console.log('stateAPIUpdate:', update);
185
186
187
    if (update) {
      // report an occured error
      if (update.error) {
188
        this.errorLogger(update.error);
189
      }
Lukas Burgey's avatar
Lukas Burgey committed
190

191
192
193
      if (!update.user) {
        this.disconnect();
        return
194
195
      }

196
      this.updateUserState(update.user_state);
197

198
199
200
201
202
203
      this.sar = update;
      this.sar$.next(update);

    } else {
      this.disconnect();
      return
Lukas Burgey's avatar
Lukas Burgey committed
204
205
206
    }
  }

207
  private fetch(): void {
Lukas Burgey's avatar
Lukas Burgey committed
208
209
210
211
212
213
    this.http.get('/backend/api/state').subscribe(
      (data: t.StateAPIResult) => this.stateAPIUpdate(data),
      (err: any) => this.errorLogger(err, 'receiving data from the server')
    );
  }

214
  // PUBLIC API
215
216
217
218
219
220
  public serviceDescription(service: t.Service): string {
    if (service.description != "") {
      return service.description;
    }
    return "No description";
  }
Lukas Burgey's avatar
Lukas Burgey committed
221

Lukas Burgey's avatar
Lukas Burgey committed
222
  public getGroups(): t.Group[] {
223
224
    if (this.sar.user.groups) {
      return this.sar.user.groups.sort(
Lukas Burgey's avatar
Lukas Burgey committed
225
226
227
228
229
230
231
232
233
234
235
236
237
238
        function(a,b) {
          if (a.name < b.name) {
            return -1;
          } else if (a.name > b.name) {
            return 1;
          } else if (a.name === b.name) {
            return 0;
          }
        }
      );
    }
    return []
  }

239
240
241
242
  public getDeployment(service: t.Service): t.Deployment {
    return this.sar.user_state.deployments.find(
      dep => dep.service && dep.service.id === service.id
    );
Lukas Burgey's avatar
Lukas Burgey committed
243
244
  }

245
246
247
248
  public getServices(group: t.Group): t.Service[] {
    // TODO implement
    //
    return []
Lukas Burgey's avatar
Lukas Burgey committed
249
250
  }

Lukas Burgey's avatar
Lukas Burgey committed
251
  public errorLogger(error: any, msg?: string): void {
252
    console.log(error);
Lukas Burgey's avatar
Lukas Burgey committed
253
254
255
256
257
    if (msg) {
      this.snackBar.open('Error: '+msg);
    } else {
      this.snackBar.open('Error');
    }
Lukas Burgey's avatar
Lukas Burgey committed
258
259
260
  }

  public login(idp: t.IdP) {
261
    this.idpService.setIdPPreference(idp);
262

263
    window.location.href = '/backend/auth/v1/request';
264
265
  }

Lukas Burgey's avatar
Lukas Burgey committed
266
  public logout() {
267
    this.http.post('/backend/auth/v1/logout', {}).subscribe(
268
      (data: t.StateAPIResult) => this.stateAPIUpdate(data),
Lukas Burgey's avatar
Lukas Burgey committed
269
270
      (err: any) => {
        this.errorLogger(err);
271
272
273
        // force manual log out
        this.loggedIn = false;
        this.loggedIn$.next(this.loggedIn);
274
275
276
277
      }
    );
  }

278
  public sentQuestionnaire(stateItemID: number, answers: Object) {
279
    return this.http.post('/backend/api/questionnaire?id='+String(stateItemID), answers).subscribe(
Lukas Burgey's avatar
Lukas Burgey committed
280
      (data: t.UserState) => {
Lukas Burgey's avatar
Lukas Burgey committed
281
        this.snackBar.open('Uploaded questionnaire');
Lukas Burgey's avatar
Lukas Burgey committed
282
        this.updateUserState(data);
Lukas Burgey's avatar
Lukas Burgey committed
283
284
285
286
      },
      (err) => {
        this.snackBar.open('Error uploading questionnaire');
        console.log(err);
287
        this.fetch();
Lukas Burgey's avatar
Lukas Burgey committed
288
289
290
291
      }
    );
  }

Lukas Burgey's avatar
Lukas Burgey committed
292
  public deleteUser() {
Lukas Burgey's avatar
Lukas Burgey committed
293
294
295
    return this.http.delete('/backend/api/delete_user').subscribe(
      (data: {deleted: boolean}) => {
        if (data && data.deleted) {
296
          this.disconnect();
Lukas Burgey's avatar
Lukas Burgey committed
297
298
          this.snackBar.open('Deleted user from server');
        }
Lukas Burgey's avatar
Lukas Burgey committed
299
300
301
302
      },
      (err) => {
        this.snackBar.open('Error deleting user from server');
        console.log(err);
303
        this.fetch();
Lukas Burgey's avatar
Lukas Burgey committed
304
305
306
      }
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

  public uploadSshKey(key: t.NewSSHKey) {
    console.log('uploading key:', key);
    const body = {
      'type': 'add',
      'key': key,
    };
    return this.http.post('/backend/api/sshkey', body).subscribe(
      (newKey: t.SSHKey) => {
        this.sshKeys.push(newKey);
        this.sshKeys$.next(this.sshKeys);
      }
    );
  }

  public removeSshKey(key: t.SSHKey) {
    console.log('deleting key:', key);
    return this.http.post('/backend/api/sshkey', {
      'type': 'remove',
      'id': key.id,
    }).subscribe(
      (data: {deleted: boolean}) => {
        if (data && data.deleted) {
          this.sshKeys = this.sshKeys.filter(
            k => key.id != k.id
          );
          this.sshKeys$.next(this.sshKeys);
        }
      }
    );
  }
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393

  public changeDeployment(action: string, group: t.Group): Observable<t.Deployment> {
    const body = {
      'type': action,
      'group': group.id,
    };
    return this.http.post('/backend/api/deployments', body).map(
      (newDep: t.Deployment) => newDep,
    );
  }

  // DATA SERVICE API
  //
  public subscribeSSHKeys(): Observable<t.SSHKey[]> {
    return this.sshKeys$.asObservable();
  }

  public subscribeLoggedIn(): Observable<boolean> {
    return this.loggedIn$.asObservable();
  }

  public subscribeState(): Observable<SAR> {
    return this.sar$.asObservable();
  }

  public subscribeUserState(): Observable<t.UserState> {
    return this.userState$.asObservable();
  }

  public subscribeSpecific<T>(selector: (state: SAR) => T): Observable<T> {
    return this.subscribeState().map(selector);
  }

  public subscribeServiceDeployment(service: t.Service): Observable<t.Deployment> {
    return this.subscribeUserState().map(
      (state: t.UserState) => {
        if (state.deployments) {
          return state.deployments.find(
            (dep: t.Deployment) => dep.service ? dep.service.id == service.id : false
          );
        }
      },
    );
  }

  public subscribeGroupDeployment(group: t.Group): Observable<t.Deployment> {
    return this.subscribeUserState().map(
      (state: t.UserState) => {
        if (state.deployments) {
          return state.deployments.find(
            (dep: t.Deployment) => dep.group == group.id
          );
        }
      },
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
394
}