Commit b53457d3 authored by Lukas Burgey's avatar Lukas Burgey

Fix interaction with the backend

parent c7091df6
<div class="mat-typography">
<h2>Credentials</h2>
<p>
You can access the service {{ stateItem.service.name }} at {{ stateItem.site.name }} using the SSH Key {{ stateItem.key.name }}.
</p>
<div *ngIf="credentialCount > 0">
<p>
Additional information about accessing the service is displayed below.
</p>
<mat-table [dataSource]="table">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.name}}</mat-cell>
</ng-container>
<ng-container matColumnDef="value">
<mat-header-cell *matHeaderCellDef>Value</mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.value }}</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="columns"></mat-header-row>
<mat-row *matRowDef="let row; columns: columns;"></mat-row>
</mat-table>
</div>
</div>
import {Component, OnInit,Inject} from '@angular/core';
import {MAT_DIALOG_DATA} from '@angular/material';
import {MatTableDataSource} from '@angular/material';
import { UserService } from '../../user.service';
import * as t from '../../types/types.module';
@Component({
selector: 'app-credentials',
templateUrl: './credentials.component.html',
styleUrls: ['./credentials.component.css']
})
export class CredentialsComponent implements OnInit {
public columns = ["name", "value"];
public stateItem: t.DeploymentStateItem;
public credentialCount: number = 0;
public table: MatTableDataSource<any>;
constructor(
public userService: UserService,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.stateItem = data.stateItem;
}
ngOnInit() {
const credentialList = [];
for (const key in this.stateItem.credentials) {
if (this.stateItem.credentials.hasOwnProperty(key)) {
credentialList.push(
{
name: key,
value: this.stateItem.credentials[key],
}
);
}
}
this.credentialCount = credentialList.length;
this.table = new MatTableDataSource(credentialList);
}
}
......@@ -5,6 +5,9 @@ import { ProfileComponent } from './profile/profile.component';
import { SshKeysComponent } from './ssh-keys/ssh-keys.component';
import { AccountComponent } from './account/account.component';
import { QuestionnaireComponent } from './questionnaire/questionnaire.component';
import { CredentialsComponent } from './credentials/credentials.component';
import * as t from '../types/types.module';
@Injectable()
export class DialogService {
......@@ -12,6 +15,7 @@ export class DialogService {
sshKeysDialog: MatDialogRef<any>;
accountDialog: MatDialogRef<any>;
questionnaireDialog: MatDialogRef<any>;
credentialsDialog: MatDialogRef<any>;
settings = {
width: '80%',
......@@ -42,12 +46,23 @@ export class DialogService {
);
}
public openQuestionnaire(taskItem: any) {
public openQuestionnaire(stateItem: t.DeploymentStateItem) {
this.questionnaireDialog = this.dialog.open(
QuestionnaireComponent,
{
data: {
taskItem: taskItem,
stateItem: stateItem,
}
}
);
}
public openCredentials(stateItem: t.DeploymentStateItem) {
this.credentialsDialog = this.dialog.open(
CredentialsComponent,
{
data: {
stateItem: stateItem,
}
}
);
......
......@@ -11,6 +11,7 @@ import {QuestionnaireComponent} from './questionnaire/questionnaire.component';
import {ProfileComponent} from './profile/profile.component';
import {SshKeysComponent} from './ssh-keys/ssh-keys.component';
import {AccountComponent} from './account/account.component';
import {CredentialsComponent} from './credentials/credentials.component';
//providers
import {DialogService} from './dialog.service';
......@@ -27,12 +28,14 @@ import {DialogService} from './dialog.service';
ProfileComponent,
SshKeysComponent,
AccountComponent,
CredentialsComponent,
],
entryComponents: [
QuestionnaireComponent,
ProfileComponent,
SshKeysComponent,
AccountComponent,
CredentialsComponent,
],
providers: [
DialogService,
......
<div class="mat-typography">
<h2>Data questionnaire</h2>
<p>
The site {{taskItem.site.name}} needs the following data to give you access to the service {{
taskItem.service.name}}.
The site {{stateItem.site.name}} needs the following data to give you access to the service {{
stateItem.service.name}}.
</p>
<form class="form-vertical" (ngSubmit)="sendAnswers()" #questionnaireForm="ngForm">
<mat-form-field *ngFor="let key of taskItem.questionnaire | ObjKeys">
<mat-form-field *ngFor="let key of stateItem.questionnaire | ObjKeys">
<input matInput required
placeholder="{{taskItem.questionnaire[key]}}" [(ngModel)]="answers[key]" name="key">
placeholder="{{stateItem.questionnaire[key]}}" [(ngModel)]="answers[key]" name="key">
</mat-form-field>
<button mat-raised-button color="primary" type="submit" [disabled]="!questionnaireForm.form.valid">Submit</button>
</form>
......
......@@ -2,6 +2,7 @@ import {Component, OnInit,Inject} from '@angular/core';
import {MAT_DIALOG_DATA} from '@angular/material';
import { UserService } from '../../user.service';
import * as t from '../../types/types.module';
@Component({
selector: 'app-questionnaire',
......@@ -10,13 +11,13 @@ import { UserService } from '../../user.service';
})
export class QuestionnaireComponent implements OnInit {
public answers: Object = {};
public taskItem: any;
public stateItem: t.DeploymentStateItem;
constructor(
public userService: UserService,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.taskItem = data.taskItem;
this.stateItem = data.stateItem;
}
ngOnInit() {
......@@ -24,7 +25,7 @@ export class QuestionnaireComponent implements OnInit {
sendAnswers() {
this.userService.sentQuestionnaire(
this.taskItem.id,
this.stateItem.id,
this.answers,
);
}
......
......@@ -38,10 +38,10 @@
<ng-template #not_uploading>
<form (ngSubmit)="uploadKey()" #sshKeyForm="ngForm">
<mat-form-field>
<input matInput placeholder="Name" required [(ngModel)]="newKeyName" name="name">
<input matInput placeholder="Name" required [(ngModel)]="formKey.name" name="name">
</mat-form-field>
<mat-form-field>
<textarea matInput placeholder="Key" required [(ngModel)]="newKeyKey" name="key"></textarea>
<textarea matInput placeholder="Key" required [(ngModel)]="formKey.key" name="key"></textarea>
</mat-form-field>
<button mat-raised-button color="primary" type="submit" [disabled]="!sshKeyForm.form.valid">Submit</button>
</form>
......
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material';
import {HttpClient} from '@angular/common/http';
import { UserService } from '../../user.service';
import { SSHKey, NewSSHKey } from '../../types/types.module';
import * as t from '../../types/types.module';
@Component({
selector: 'app-ssh-keys',
templateUrl: './ssh-keys.component.html',
......@@ -11,32 +14,74 @@ import { SSHKey, NewSSHKey } from '../../types/types.module';
})
export class SshKeysComponent implements OnInit {
columns = ['name', 'key', 'action'];
// does the user want to upload a key?
upload: boolean;
newKeyName: string;
newKeyKey: string;
formKey: t.NewSSHKey;
constructor(
public userService: UserService
) { }
public userService: UserService,
public http: HttpClient,
) {
}
ngOnInit() {
this.upload = false;
this.newKeyName = '';
this.newKeyKey = '';
this.formKey = {
name: '',
key: '',
}
}
public uploadKey() {
const newKey: NewSSHKey = {
name: this.newKeyName,
key: this.newKeyKey,
private uploadSshKey(key: t.NewSSHKey) {
const body = {
'type': 'add',
'key': key,
};
this.userService.uploadSshKey(newKey);
return this.http.post('/backend/api/sshkey', body).subscribe(
(newKey: t.SSHKey) => {
if (newKey) {
this.userService.user.ssh_keys.push(newKey);
}
},
(err) => {
console.log(err);
this.userService.update();
}
);
}
private removeSshKey(key: t.SSHKey) {
return this.http.post('/backend/api/sshkey', {
'type': 'remove',
'id': key.id,
}).subscribe(
(data: {deleted: boolean}) => {
if (data && data.deleted) {
this.userService.user.ssh_keys = this.userService.user.ssh_keys.filter(
k => key.id != k.id
);
}
},
(err) => {
console.log(err);
this.userService.update();
}
);
}
public _sshKeyTable(): MatTableDataSource<any> {
return new MatTableDataSource(this.userService.user.ssh_keys);
}
public uploadKey() {
this.uploadSshKey(this.formKey);
this.ngOnInit();
}
public deleteKey(key: SSHKey) {
this.userService.removeSshKey(key);
this.removeSshKey(key);
this.ngOnInit();
}
}
......
<div *ngIf="userService.loggedIn()">
<div style="padding-bottom: 30px;">
<h2>Services</h2>
<mat-accordion *ngIf="userService.deploymentIDs.length > 0">
<app-service *ngFor="let dID of userService.deploymentIDs" [deployment]="userService.getDeployment(dID)"></app-service>
<mat-accordion *ngIf="userService.services.length > 0">
<app-service *ngFor="let service of userService.services" [deployment]="userService.getDeployment(service)" [service]="service"></app-service>
</mat-accordion>
<p *ngIf="userService.deploymentIDs.length === 0">
<p *ngIf="userService.services.length === 0">
You have no available services.<br/>
This is due services requiring users to be member of a certain group.
</p>
......
<mat-expansion-panel *ngIf="deployment">
<mat-expansion-panel *ngIf="service">
<mat-expansion-panel-header>
<mat-panel-title>{{ service.name }}</mat-panel-title>
<mat-panel-description>{{ service.description }}</mat-panel-description>
......@@ -16,11 +16,12 @@
[ngSwitch]="taskState(site, key)" class="childs-inline">
<!-- states with actions -->
<button *ngSwitchCase="'deployed'" mat-button mat-icon-button
(click)="dialog.openCredentials(taskItem(site, key))"
matTooltip="The key {{key.name}} is deployed to the site. Click to see details.">
<mat-icon style="vertical-align: middle">call_made</mat-icon>
</button>
<button *ngSwitchCase="'questionnaire'" mat-button mat-icon-button
(click)="dialog.openQuestionnaire(userService.taskItem(site, service))"
(click)="dialog.openQuestionnaire(taskItem(site, y))"
matTooltip="This site needs more data to deploy the keys. Please click to submit the data.">
<mat-icon>warning</mat-icon>
</button>
......@@ -31,18 +32,18 @@
<span *ngSwitchCase="'removal_pending'" mat-icon-button matTooltip="Waiting for the removal of the key {{ key.name }} from the site">
<mat-progress-spinner diameter="24" mode="indeterminate"></mat-progress-spinner>
</span>
<span *ngSwitchCase="'not_deployed'" mat-icon-button matTooltip="The key {{ key.name }} is not deployed to this site.">
<button *ngSwitchCase="'not_deployed'" mat-icon-button matTooltip="The key {{ key.name }} is not deployed to this site.">
<mat-icon style="vertical-align: middle">call_received</mat-icon>
</span>
<span *ngSwitchCase="'failed'" mat-icon-button matTooltip="This site failed to deploy the credentials. The deployment will be retried.">
</button>
<button *ngSwitchCase="'failed'" mat-icon-button matTooltip="This site failed to deploy the credentials. The deployment will be retried.">
<mat-icon style="vertical-align: middle">error</mat-icon>
</span>
<span *ngSwitchCase="'rejected'" mat-icon-button matTooltip="This site rejected the deployment of the key {{ key.name }}.">
</button>
<button *ngSwitchCase="'rejected'" mat-icon-button matTooltip="This site rejected the deployment of the key {{ key.name }}.">
<mat-icon style="vertical-align: middle">error</mat-icon>
</span>
<!-- hacky solution -->
<span *ngSwitchDefault mat-icon-button matTooltip="Receiving update from backend">
<mat-progress-spinner diameter="24" mode="indeterminate"></mat-progress-spinner>
</button>
<!-- if we have no deployment -> assume state == not_deployed -->
<span *ngSwitchDefault mat-icon-button matTooltip="The key {{ key.name }} is not deployed to this site.">
<mat-icon style="vertical-align: middle">call_received</mat-icon>
</span>
</span>
</span>
......@@ -50,11 +51,11 @@
</td>
</tr>
<tr>
<td style="padding-right: 35px;"
*ngFor="let group of service.groups"
matTooltip="Can be used with membership of group {{ group.name }}">
<td *ngFor="let group of service.groups" style="margin-right: 15px;">
<span matTooltip="Can be used with membership of group {{ group.name }}">
<mat-icon style="vertical-align: middle; padding-right: 5px;">lock outline</mat-icon>
{{ group.name }}
</span>
</td>
</tr>
</table>
......
......@@ -14,8 +14,8 @@ import * as t from '../types/types.module';
styleUrls: ['./service.component.css']
})
export class ServiceComponent implements OnInit {
@Input() service: t.Service;
@Input() deployment: t.Deployment;
public service: t.Service;
constructor(
public userService: UserService,
......@@ -25,7 +25,6 @@ export class ServiceComponent implements OnInit {
}
ngOnInit() {
this.service = this.deployment.service;
}
public isDeployed(key: t.SSHKeyRef): boolean {
......
......@@ -51,12 +51,16 @@ export interface Service {
group: Group[];
}
interface JSONObject {
[key: string]: string;
}
export interface DeploymentStateItem {
id: number;
site: Site;
state: string;
questionnaire: any;
credentials: any;
questionnaire: JSONObject;
credentials: JSONObject;
key: SSHKeyRef;
service: Service;
}
......
......@@ -24,8 +24,8 @@ export class UserService {
public userState: t.UserState;
public messages: string[] = [];
// local copy of deployments
public deploymentIDs: number[] = [];
// local copy of services
public services: t.Service[] = [];
constructor(
......@@ -94,14 +94,24 @@ export class UserService {
this.user = newUser;
}
private updateDeployments(newDeployments: t.Deployment[]){
if (this.deploymentIDs.length !== newDeployments.length) {
for (let dep of newDeployments) {
this.deploymentIDs.push(dep.id);
private updateServices(newServices: t.Service[]){
if (this.services.length !== newServices.length) {
this.services = [];
for (let service of newServices) {
this.services.push(service);
}
}
}
private _logout() {
this._loggedIn = false;
this.snackBar.open('Logged out');
this._stompService.disconnect();
this.services = [];
this.user = null;
this.userState = null;
}
private updateUserState(newState: t.UserState) {
// did a login occur?
let login = (!this._loggedIn && newState);
......@@ -112,27 +122,31 @@ export class UserService {
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._logout()
return
}
this.updateDeployments(newState.deployments);
this.userState = newState;
}
private stateAPIUpdate(update: t.StateAPIResult) {
if (update) {
this.updateUser(update.user)
this.updateUserState(update.user_state);
this.updateServices(update.services);
// report an occured error
if (update.error) {
this.snackBar.open(update.error);
}
} else {
console.log("Got null update");
}
}
private setIdPPreference(idp: t.IdP) {
......@@ -140,10 +154,10 @@ export class UserService {
}
// PUBLIC API
public getDeployment(id: number): t.Deployment {
public getDeployment(service: t.Service): t.Deployment {
return this.userState.deployments.find(
dep => {
return dep.id == id
return dep.service.id === service.id;
}
);
}
......@@ -225,49 +239,16 @@ export class UserService {
this.http.post('/backend/auth/v1/logout', {}).subscribe(
(data: t.StateAPIResult) => {
this.stateAPIUpdate(data);
}
);
}
public uploadSshKey(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);
this.user.ssh_keys = this.user.ssh_keys.filter(
k => key.id == k.id
);
},
(err) => {
this.snackBar.open('Error deleting key');
console.log(err);
this.update();
this._logout();
}
);
}
public sentQuestionnaire(taskItemID: number, answers: any) {
return this.http.post('/backend/api/questionnaire?id='+String(taskItemID), answers).subscribe(
public sentQuestionnaire(stateItemID: number, answers: any) {
return this.http.post('/backend/api/questionnaire?id='+String(stateItemID), answers).subscribe(
(data: t.UserState) => {
this.snackBar.open('Uploaded questionnaire');
//this.snackBar.open('Deployed key ' + key.name);
......
......@@ -23,7 +23,7 @@ body {
}
.mat-column-name {
max-width: 100px;
max-width: 200px;
}
.mat-column-action {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment