import {Injectable} from '@angular/core'; import {HttpClient, HttpErrorResponse} from '@angular/common/http'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/of'; import {CookieService} from 'ngx-cookie-service'; import {MatTableDataSource} from '@angular/material'; import {SnackBarService} from './snackbar.service'; import {StompConfig, StompRService} from '@stomp/ng2-stompjs'; import {Message} from '@stomp/stompjs'; import {environment} from '../environments/environment'; import * as t from './types/types.module'; @Injectable() export class UserService { private _loggedIn: boolean = false; public user: t.User; public userState: t.UserState; public messages: string[] = []; public services: t.Service[]; constructor( public cookieService: CookieService, public http: HttpClient, public snackBar: SnackBarService, private _stompService: StompRService, ) { this.update(); } // PRIVATE API private initStomp() { // handle with care let login = this.user.id let passcode = this.cookieService.get('sessionid'); const stompConfig: StompConfig = { // Which server? url: 'wss://hdf-portal.data.kit.edu/ws', // Headers // Typical keys: login, passcode, host headers: { login: 'webpage-client:' + login, passcode: passcode, }, // 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 // Typical value 5000 (5 seconds) reconnect_delay: 5000, // Will log diagnostics on console debug: false, }; this._stompService.config = stompConfig; this._stompService.initAndConnect(); } private connectLiveUpdates() { this.initStomp(); let subscription = this._stompService.subscribe( '/exchange/update/' + this.user.id.toString() ); subscription.map((message: Message) => { return message.body; }).subscribe((body: any) => { let json = JSON.parse(body); if (json.message && json.message != '') { this.snackBar.open(json.message); this.messages.push(json.message); } if (json.user_state) { this.updateUserState(json.user_state); } }); } private updateUser(newUser: t.User) { this.user = newUser; } private updateUserState(newState: t.UserState) { // did a login occur? let login = (!this._loggedIn && newState); // did a logout occur? let logout = (this._loggedIn && !newState); // -- Value updating -- if (login) { this._loggedIn = true; this.snackBar.open('Logged in'); this.connectLiveUpdates() } if (logout) { this._loggedIn = false; this.snackBar.open('Logged out'); this._stompService.disconnect(); } this.userState = newState; } private stateAPIUpdate(update: t.StateAPIResult) { this.updateUser(update.user) this.updateUserState(update.user_state); this.services = update.services; // report an occured error if (update.error) { this.snackBar.open(update.error); } } private setIdPPreference(idp: t.IdP) { this.cookieService.set(environment.idpCookieName, String(idp.id)); } // PUBLIC API public userInfo(): MatTableDataSource { const userInfoList = []; for (const key in this.user.userinfo) { if (this.user.userinfo.hasOwnProperty(key)) { userInfoList.push({name: key, info: this.user.userinfo[key]}); } } return new MatTableDataSource(userInfoList); } public sshKeyTable(): MatTableDataSource { return new MatTableDataSource(this.user.ssh_keys); } public loggedIn():boolean { if (this.userState) { return true; } return false; } public update() { this.http .get('/backend/api/state') .subscribe( (data: t.StateAPIResult) => { this.stateAPIUpdate(data); }, (err: HttpErrorResponse) => { console.log('Error', err); this.snackBar.open('Error receiving data from the server'); } ); } public errorHandler(error: any): void { if (error.status === 500) { this.snackBar.open('Server Error'); } } public getIdPPreference(): Observable { let idpID = Number(this.cookieService.get(environment.idpCookieName)); return this.http.get('/backend/auth/v1/info').map( (authInfo: t.AuthInfo) => { let selected = authInfo.idps[1]; if (!idpID) { idpID = authInfo.default; } for (const idp of authInfo.idps) { if (idp.id === idpID) { selected = idp; } } return { idps: authInfo.idps, selected: selected, }; } ).catch( (error: any) => { this.errorHandler(error); return Observable.of(null); } ); } public login(idp: t.IdP) { this.setIdPPreference(idp); window.location.href = 'https://hdf-portal.data.kit.edu/backend/auth/v1/request'; } public logout() { this.http.post('/backend/auth/v1/logout', {}).subscribe( (data: t.StateAPIResult) => { this.stateAPIUpdate(data); } ); } public addSshKey(key: t.NewSSHKey) { const body = { 'type': 'add', 'key': key, }; return this.http.post('/backend/api/sshkey', body).subscribe( (data: t.UserState) => { this.updateUserState(data); }, (err) => { this.snackBar.open('Error uploading key'); console.log(err); this.update(); } ); } public removeSshKey(key: t.SSHKey) { const body = { 'type': 'remove', 'id': key.id, }; return this.http.post('/backend/api/sshkey', body).subscribe( (data: t.UserState) => { this.updateUserState(data); }, (err) => { this.snackBar.open('Error deleting key'); console.log(err); this.update(); } ); } public addDeployment(service: t.Service, key: t.SSHKey) { const body = { 'type': 'add', 'key': key.id, 'service': service.id, }; return this.http.post('/backend/api/deployments', body).subscribe( (data: t.UserState) => { //this.snackBar.open('Deployed key ' + key.name); this.updateUserState(data); }, (err) => { this.snackBar.open('Error deploying key ' + key.name); console.log(err); this.update(); } ); } public removeDeployment(service: t.Service, key: t.SSHKey) { const body = { 'type': 'remove', 'key': key.id, 'service': service.id, }; return this.http.post('/backend/api/deployments', body).subscribe( (data: t.UserState) => { //this.snackBar.open('Withdrew key ' + key.name); this.updateUserState(data); }, (err) => { this.snackBar.open('Error withdrawing key ' + key.name); console.log(err); this.update(); } ); } public sentQuestionnaire(taskItemID: number, answers: any) { return this.http.post('/backend/api/questionnaire?id='+String(taskItemID), answers).subscribe( (data: t.UserState) => { this.snackBar.open('Uploaded questionnaire'); //this.snackBar.open('Deployed key ' + key.name); this.updateUserState(data); }, (err) => { this.snackBar.open('Error uploading questionnaire'); console.log(err); this.update(); } ); } public deleteUser() { return this.http.delete('/backend/api/delete_user/').subscribe( (data: t.UserState) => { this.snackBar.open('Deleted user from server'); this.updateUserState(data); }, (err) => { this.snackBar.open('Error deleting user from server'); console.log(err); this.update(); } ); } public taskItem(site: t.Site, service: t.Service, key: t.SSHKeyRef): t.DeploymentStateItem { if (this.userState.deployment_state_items && site && service && key) { for (const item of this.userState.deployment_state_items){ if (item.site.id == site.id && item.key.id == key.id && item.service.id == service.id) { return item; } } } return null; } public taskState(site: t.Site, service: t.Service, key: t.SSHKeyRef): string { let item = this.taskItem(site, service, key); if (item !== null) { return item.state; } return ""; } }