user.service.ts 10.1 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
  private connect(): void {
    this.subscribeState().subscribe(
      (state: SAR) => {
Lukas Burgey's avatar
Lukas Burgey committed
61
        console.log('State:', state);
62
63
64
65
66
67
68
69
70
71

        // 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) {
Lukas Burgey's avatar
Lukas Burgey committed
150
          this.userStateUpdate(update.user_state);
Lukas Burgey's avatar
Lukas Burgey committed
151
        }
Lukas Burgey's avatar
Lukas Burgey committed
152
153
154
      },
      (error: any) => {
        console.log(error)
155
      }
Lukas Burgey's avatar
Lukas Burgey committed
156
    );
Lukas Burgey's avatar
Lukas Burgey committed
157
  }
158

Lukas Burgey's avatar
Lukas Burgey committed
159
160
161
  private userStateUpdate(newState: t.UserState): void {
    console.log('userStateUpdate:', newState);

Lukas Burgey's avatar
Lukas Burgey committed
162
      // did a login occur?
163
    let login = (!this.loggedIn && newState);
Lukas Burgey's avatar
Lukas Burgey committed
164
    // did a logout occur?
165
    let logout = (this.loggedIn && !newState);
Lukas Burgey's avatar
Lukas Burgey committed
166
167
168

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

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

Lukas Burgey's avatar
Lukas Burgey committed
184
  private stateAPIUpdate(update: t.StateAPIResult) {
185
    console.log('stateAPIUpdate:', update);
Lukas Burgey's avatar
Lukas Burgey committed
186

187
188
189
    if (update) {
      // report an occured error
      if (update.error) {
190
        this.errorLogger(update.error);
191
      }
Lukas Burgey's avatar
Lukas Burgey committed
192

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

Lukas Burgey's avatar
Lukas Burgey committed
198
      this.userStateUpdate(update.user_state);
199

200
201
202
203
204
205
      this.sar = update;
      this.sar$.next(update);

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

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

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

Lukas Burgey's avatar
Lukas Burgey committed
224
  public getGroups(): t.Group[] {
225
226
    if (this.sar.user.groups) {
      return this.sar.user.groups.sort(
Lukas Burgey's avatar
Lukas Burgey committed
227
228
229
230
231
232
233
234
235
236
237
238
239
240
        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 []
  }

241
242
243
244
  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
245
246
  }

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

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

  public login(idp: t.IdP) {
263
    this.idpService.setIdPPreference(idp);
264

265
    window.location.href = '/backend/auth/v1/request';
266
267
  }

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

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

Lukas Burgey's avatar
Lukas Burgey committed
294
  public deleteUser() {
Lukas Burgey's avatar
Lukas Burgey committed
295
296
297
    return this.http.delete('/backend/api/delete_user').subscribe(
      (data: {deleted: boolean}) => {
        if (data && data.deleted) {
298
          this.disconnect();
Lukas Burgey's avatar
Lukas Burgey committed
299
300
          this.snackBar.open('Deleted user from server');
        }
Lukas Burgey's avatar
Lukas Burgey committed
301
302
303
304
      },
      (err) => {
        this.snackBar.open('Error deleting user from server');
        console.log(err);
305
        this.fetch();
Lukas Burgey's avatar
Lukas Burgey committed
306
307
308
      }
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
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
338
339

  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);
        }
      }
    );
  }
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
394
395

  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
396
}