user.service.ts 9.11 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
import { throwError as observableThrowError,  Observable, BehaviorSubject, of, EMPTY } from 'rxjs';
4
import { map, catchError } from 'rxjs/operators';
Lukas Burgey's avatar
Lukas Burgey committed
5

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

import { SnackBarService } from './snackbar.service';
import { IdpService } from './idp.service';
Lukas Burgey's avatar
Lukas Burgey committed
12

13
import { VO, User, Update, State, Deployment, SSHKey, NewSSHKey, IdP, Service } from './types/types.module';
Lukas Burgey's avatar
Lukas Burgey committed
14

Lukas Burgey's avatar
Lukas Burgey committed
15
16
17

@Injectable()
export class UserService {
18
19
  private initialized = false;
  private loggedIn: boolean = false;
Lukas Burgey's avatar
Lukas Burgey committed
20

21
  private user: User;
Lukas Burgey's avatar
Lukas Burgey committed
22
  private user$: BehaviorSubject<User>;
Lukas Burgey's avatar
Lukas Burgey committed
23

24
  private sshKeys: SSHKey[] = new Array<SSHKey>();
Lukas Burgey's avatar
Lukas Burgey committed
25
  private sshKeys$ = new BehaviorSubject<SSHKey[]>([]);
Lukas Burgey's avatar
Lukas Burgey committed
26

27
  private deployments: Map<number, Deployment> = new Map([]);
Lukas Burgey's avatar
Lukas Burgey committed
28
  private deployments$ = new BehaviorSubject<Deployment[]>([]);
Lukas Burgey's avatar
Lukas Burgey committed
29

30
  public voSelector = (user: User) => user ? user.vos : [];
31
  public serviceSelector = (user: User) => user ? user.services ? user.services : [] : [];
Lukas Burgey's avatar
Lukas Burgey committed
32
33

  constructor(
Lukas Burgey's avatar
Lukas Burgey committed
34
35
36
37
    private cookieService: CookieService,
    private http: HttpClient,
    private snackBar: SnackBarService,
    private idpService: IdpService,
Lukas Burgey's avatar
Lukas Burgey committed
38
    private stompService: StompRService,
Lukas Burgey's avatar
Lukas Burgey committed
39
  ) {
Lukas Burgey's avatar
Lukas Burgey committed
40
    this.user$ = new BehaviorSubject(null);
41
    this.connect();
42
43
  }

44
45
  // PRIVATE API
  private connect(): void {
Lukas Burgey's avatar
Lukas Burgey committed
46
47
    this.fetch();

48
49
    this.subscribeUser().subscribe(
      (newUser: User) => {
50
        console.log('user$:', newUser);
Lukas Burgey's avatar
Lukas Burgey committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

        if (newUser == undefined || newUser == null) {
          // LOGGED OUT
          // show the logout to the user
          if (this.loggedIn) {
            this.snackBar.open('Logged out');

            // purge all values
            this.stompService.disconnect();
            this.user = undefined;
            this.deployments = new Map([]);
            this.deployments$.next([]);
            this.sshKeys = [];
            this.sshKeys$.next([]);
          }


          this.loggedIn =  false;
        } else {
          // LOGGED IN
          // show the login to the user
          if (!this.loggedIn) {
            this.snackBar.open('Logged in');
          }

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

          if (newUser.id) {
            this.connectLiveUpdates(newUser.id);
          }
          if (newUser.ssh_keys) {
            this.sshKeys = newUser.ssh_keys;
            this.sshKeys$.next(this.sshKeys);
          }
          if (newUser.deployments) {
            newUser.deployments.forEach(
              (newDep: Deployment) => {
                this.deployments.set(newDep.id,  newDep);
              }
            );
            this.deployments$.next(Array.from(this.deployments.values()));
          }

Lukas Burgey's avatar
Lukas Burgey committed
93
          this.loggedIn = true;
94
95
96
          this.initialized = true;
        }
      },
Lukas Burgey's avatar
Lukas Burgey committed
97
98
      this.handleError(true),
      () => console.log('user$ is complete'),
99
    );
Lukas Burgey's avatar
Lukas Burgey committed
100
101
  }

102
  private connectLiveUpdates(userID: number): void {
Lukas Burgey's avatar
Lukas Burgey committed
103
    // handle with care
104
    let login = userID
Lukas Burgey's avatar
Lukas Burgey committed
105
    let passcode = this.cookieService.get('sessionid');
Lukas Burgey's avatar
Lukas Burgey committed
106

Lukas Burgey's avatar
Lukas Burgey committed
107
108
    const stompConfig: StompConfig = {
      // Which server?
109
      url: 'wss://'+window.location.host+'/ws',
110

Lukas Burgey's avatar
Lukas Burgey committed
111
112
113
114
115
116
      // Headers
      // Typical keys: login, passcode, host
      headers: {
        login: 'webpage-client:' + login,
        passcode: passcode,
      },
Lukas Burgey's avatar
Lukas Burgey committed
117

Lukas Burgey's avatar
Lukas Burgey committed
118
119
120
121
122
123
      // 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
124
125
      // Typical value 15000 (15 seconds)
      reconnect_delay: 15000,
126

Lukas Burgey's avatar
Lukas Burgey committed
127
128
129
      // Will log diagnostics on console
      debug: false,
    };
Lukas Burgey's avatar
Lukas Burgey committed
130
131
    this.stompService.config = stompConfig;
    this.stompService.initAndConnect();
Lukas Burgey's avatar
Lukas Burgey committed
132

Lukas Burgey's avatar
Lukas Burgey committed
133
    let subscription = this.stompService.subscribe(
134
      '/exchange/users/' + userID.toString()
Lukas Burgey's avatar
Lukas Burgey committed
135
    );
136

Lukas Burgey's avatar
Lukas Burgey committed
137
138
    subscription.subscribe(
      (message: Message) => {
139
140
141
        let update: Update = JSON.parse(message.body);
        console.log('update:', update);

Lukas Burgey's avatar
Lukas Burgey committed
142
143
        if (update.error && update.error != '') {
          this.snackBar.open(update.error);
Lukas Burgey's avatar
Lukas Burgey committed
144
        }
145
146
147

        if (update.deployment) {
          this.updateDeployment(update.deployment);
Lukas Burgey's avatar
Lukas Burgey committed
148
        }
Lukas Burgey's avatar
Lukas Burgey committed
149
      },
150
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
151
    );
Lukas Burgey's avatar
Lukas Burgey committed
152
  }
153

Lukas Burgey's avatar
Lukas Burgey committed
154
155
156
157
158
159
  private updateDeployment(dep: Deployment): void {
    if (dep) {
      this.deployments.set(dep.id, dep);
      this.deployments$.next(Array.from(this.deployments.values()));
    }
  }
Lukas Burgey's avatar
Lukas Burgey committed
160

Lukas Burgey's avatar
Lukas Burgey committed
161
  private updateState(update: State): void {
162
163
164
    if (update) {
      // report an occured error
      if (update.error) {
165
        this.snackBar.open(update.error);
166
      }
Lukas Burgey's avatar
Lukas Burgey committed
167

Lukas Burgey's avatar
Lukas Burgey committed
168
169
      this.user = update.user;
      this.user$.next(this.user);
170
171

    } else {
Lukas Burgey's avatar
Lukas Burgey committed
172
      this.user$.next(undefined);
Lukas Burgey's avatar
Lukas Burgey committed
173
174
175
    }
  }

176
  private fetch(): void {
Lukas Burgey's avatar
Lukas Burgey committed
177
    this.http.get<State>('/backend/api/state').subscribe(
178
      (state: State) => this.updateState(state),
Lukas Burgey's avatar
Lukas Burgey committed
179
      this.handleError(false, 'Error fetching state. Try again later'),
Lukas Burgey's avatar
Lukas Burgey committed
180
181
182
    );
  }

Lukas Burgey's avatar
Lukas Burgey committed
183
184
185
186
187
188
  private handleError(fetch: boolean, msg?: string) {
    return (error: any) => {
      if (error.status == 403) {
        this.login();
        return
      }
189

Lukas Burgey's avatar
Lukas Burgey committed
190
191
192
193
194
195
196
      if (msg) {
        this.snackBar.open(msg);
      }
      console.log('fetch:', error);
      if (fetch) {
        this.fetch();
      }
197
198
    }
  }
Lukas Burgey's avatar
Lukas Burgey committed
199

Lukas Burgey's avatar
Lukas Burgey committed
200
201
202
  private catchError(fetch: boolean, msg?: string) {
    return (error: any) => {
      this.handleError(fetch, msg)(error);
203
      return EMPTY;
Lukas Burgey's avatar
Lukas Burgey committed
204
    }
Lukas Burgey's avatar
Lukas Burgey committed
205
206
  }

Lukas Burgey's avatar
Lukas Burgey committed
207
  private logErrorAndFetch(error: any)  {
208
209
    if (error.status === 403) {
      this.login();
Lukas Burgey's avatar
Lukas Burgey committed
210
    }
211
212
213
214

    console.log(error);
    this.snackBar.open('Error');
    this.fetch();
Lukas Burgey's avatar
Lukas Burgey committed
215
    return observableThrowError(error);
Lukas Burgey's avatar
Lukas Burgey committed
216
217
  }

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

226
227
228
229
  public login(idp?: IdP): void {
    if (idp) {
      this.idpService.setIdPPreference(idp);
    }
230

231
    window.location.href = '/backend/auth/v1/request';
232
233
  }

Lukas Burgey's avatar
Lukas Burgey committed
234
  public logout(): void {
Lukas Burgey's avatar
Lukas Burgey committed
235
    this.http.post<State>('/backend/auth/v1/logout', undefined).subscribe(
236
      (state: State) => this.updateState(state),
Lukas Burgey's avatar
Lukas Burgey committed
237
238
239
      (err: HttpErrorResponse) => {
        this.updateState(undefined);
      }
240
241
242
    );
  }

243
  public sentQuestionnaire(stateItemID: number, answers: Object) {
Lukas Burgey's avatar
Lukas Burgey committed
244
245
    return this.http.post<Deployment>('/backend/api/questionnaire?id='+String(stateItemID), answers).subscribe(
      (dep: Deployment) => this.updateDeployment(dep),
246
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
247
248
249
    );
  }

Lukas Burgey's avatar
Lukas Burgey committed
250
  public deleteUser() {
Lukas Burgey's avatar
Lukas Burgey committed
251
252
253
    return this.http.delete('/backend/api/delete_user').subscribe(
      (data: {deleted: boolean}) => {
        if (data && data.deleted) {
Lukas Burgey's avatar
Lukas Burgey committed
254
          this.user$.next(undefined);
Lukas Burgey's avatar
Lukas Burgey committed
255
256
          this.snackBar.open('Deleted user from server');
        }
Lukas Burgey's avatar
Lukas Burgey committed
257
      },
258
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
259
260
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
261

Lukas Burgey's avatar
Lukas Burgey committed
262
263
  public uploadSshKey(formData: FormData): void {
    this.http.post<SSHKey>('/backend/api/sshkey', formData).subscribe(
264
      (newKey: SSHKey) => {
Lukas Burgey's avatar
Lukas Burgey committed
265
266
        this.sshKeys.push(newKey);
        this.sshKeys$.next(this.sshKeys);
267
      },
268
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
269
270
271
    );
  }

272
  public removeSshKey(key: SSHKey) {
Lukas Burgey's avatar
Lukas Burgey committed
273
    console.log('Deleting key:', key);
Lukas Burgey's avatar
Lukas Burgey committed
274
275
276
277
278
279
280
281
282
283
284
    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);
        }
285
      },
286
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
287
288
    );
  }
289

290
  public changeDeployment(action: string, vo: VO): Observable<Deployment> {
291
292
    const body = {
      'type': action,
293
      'vo': vo.id,
294
    };
295
296
    return this.http.post<Deployment>('/backend/api/deployments', body).pipe(
      catchError(this.catchError(true, "Error changing deployment")),
297
298
299
300
301
    );
  }

  // DATA SERVICE API
  //
Lukas Burgey's avatar
Lukas Burgey committed
302
  public subscribeSpecific<T>(selector: (user: User) => T): Observable<T> {
303
    return this.subscribeUser().pipe(map(selector));
304
305
  }

Lukas Burgey's avatar
Lukas Burgey committed
306
307
  public subscribeSSHKeys(): Observable<SSHKey[]> {
    return this.sshKeys$.asObservable();
308
309
  }

310
311
  public subscribeUser(): Observable<User> {
    return this.user$.asObservable();
312
313
  }

314
315
  public subscribeDeployments(): Observable<Deployment[]> {
    return this.deployments$.asObservable();
316
317
  }

318
319
320
  public subscribeVOs(): Observable<VO[]> {
    let voSelector = (user: User) => user ? user.vos : [];
    return this.subscribeSpecific<VO[]>(voSelector);
321
322
  }

323
  public subscribeServiceDeployment(service: Service): Observable<Deployment> {
324
    return this.subscribeDeployments().pipe(
Lukas Burgey's avatar
Lukas Burgey committed
325
      map((deployments: Deployment[]) => {
326
327
          return deployments.find(
            (dep: Deployment) => dep.service ? dep.service.id == service.id : false
328
329
          );
        }
330
      ),
331
332
333
    );
  }

334
  public subscribeVODeployment(vo: VO): Observable<Deployment> {
335
336
337
    return this.subscribeDeployments().pipe(
      map((deployments: Deployment[]) => {
          return deployments.find(
Lukas Burgey's avatar
Lukas Burgey committed
338
            (dep: Deployment) => dep.vo.id == vo.id
339
340
341
          );
        }
      ),
342
343
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
344
}