Commit 6c318502 authored by Lukas Burgey's avatar Lukas Burgey

Add service deployments

Enhance the user service API.
Closes #3
parent 4d2987ed
...@@ -30,6 +30,7 @@ import { BodyComponent } from './body/body.component'; ...@@ -30,6 +30,7 @@ import { BodyComponent } from './body/body.component';
import { HeaderComponent } from './header/header.component'; import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component'; import { FooterComponent } from './footer/footer.component';
import { VoDataComponent } from './vo-data/vo-data.component'; import { VoDataComponent } from './vo-data/vo-data.component';
import { ServiceComponent } from './service/service.component';
@NgModule({ @NgModule({
...@@ -54,6 +55,7 @@ import { VoDataComponent } from './vo-data/vo-data.component'; ...@@ -54,6 +55,7 @@ import { VoDataComponent } from './vo-data/vo-data.component';
BodyComponent, BodyComponent,
FooterComponent, FooterComponent,
VoDataComponent, VoDataComponent,
ServiceComponent,
], ],
providers: [ providers: [
CookieService, CookieService,
......
<div class="body" class="mat-typography" class="body" > <div class="body" class="mat-typography" class="body" >
<div *ngIf="(userService.subscribeUser() | async) as user" style="max-width: 800px; margin: auto;"> <div *ngIf="(userService.subscribeUser() | async) as user" style="max-width: 800px; margin: auto;">
<div *ngIf="(userService.subscribeSSHKeys() | async)?.length > 0; else noCredentials"> <div *ngIf="(userService.subscribeSSHKeys() | async)?.length > 0; else noCredentials">
<h3>Your Virtual Organisations</h3> <div style="margin-bottom: 50px;">
<h3>Your services</h3>
<mat-accordion *ngIf="(userService.subscribeServices() | async) as services; else noServices">
<app-service *ngFor="let service of services" [service]="service"></app-service>
</mat-accordion>
<ng-template #noServices>
<p>
You are not permitted to use any services.
</p>
</ng-template>
</div>
<div> <div>
<mat-accordion *ngIf="(userService.subscribeVOs() | async) as vos"> <h3>Your Virtual Organisations</h3>
<mat-accordion *ngIf="(userService.subscribeVOs() | async) as vos; else noVOs">
<app-vo-data *ngFor="let vo of vos" [vo]="vo"></app-vo-data> <app-vo-data *ngFor="let vo of vos" [vo]="vo"></app-vo-data>
</mat-accordion> </mat-accordion>
<ng-template #noVOs> <ng-template #noVOs>
......
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title style="min-width: 100px;">{{ service.name }}</mat-panel-title>
<mat-panel-description *ngIf="(deployment$ | async) as dep">
<span *ngIf="dep.state_target == 'deployed'">
Access requested
</span>
</mat-panel-description>
</mat-expansion-panel-header>
<!-- expansion body -->
<div>
<table>
<thead>
<tr>
<td>Site</td>
<td>Service</td>
<td>State</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<span matTooltip="Site {{ site.name }} provides the service {{ service.name }}.">
<mat-icon>account_balance</mat-icon>
{{ site.name }}
</span>
</td>
<td>
<span matTooltip="{{ service.description }}">
<mat-icon>web</mat-icon>
{{ service.name }}
</span>
</td>
<ng-container *ngIf="(deploymentState$ | async) as stateItem; else noStateItem">
<td>
<span [ngSwitch]="stateItem.state" class="spaced">
<mat-icon *ngSwitchCase="'deployed'" matTooltip="The credentials are deployed for the service {{ service.name }}. Click to see details.">call_made</mat-icon>
<mat-icon *ngSwitchCase="'questionnaire'" matTooltip="This site needs more data to deploy the keys. Please click to submit the data.">warning</mat-icon>
<mat-progress-spinner *ngSwitchCase="'deployment_pending'" diameter="24" mode="indeterminate" matTooltip="Waiting for the deployment of the credentials to the site {{ site.name }}"></mat-progress-spinner>
<mat-progress-spinner *ngSwitchCase="'removal_pending'" diameter="24" mode="indeterminate" matTooltip="Waiting for the removal of the credentials from the site {{ site.name }}"></mat-progress-spinner>
<mat-icon *ngSwitchCase="'not_deployed'" mat-icon-button matTooltip="The credentials are not deployed for the service {{ service.name }}.">call_received</mat-icon>
<mat-icon *ngSwitchCase="'failed'" mat-icon-button matTooltip="Site {{ site.name }} failed to deploy the credentials. The deployment will be retried. Click for details.">error</mat-icon>
<mat-icon *ngSwitchCase="'rejected'" mat-icon-button matTooltip="Site {{ site.name }} rejected the deployment of the credentials. Click for details.">error</mat-icon>
<mat-icon *ngSwitchDefault mat-icon-button matTooltip="Access to this service was never requested.">call_received</mat-icon>
</span>
{{ lang.printState(stateItem.state) }}
</td>
<td *ngIf="stateItem.state === 'questionaire'">
<button (click)="dialog.openQuestionnaire(stateItem)" mat-raised-button class="mat-elevation-z6"> Questionnaire </button>
</td>
<td *ngIf="stateItem.state === 'deployed'">
<button (click)="dialog.openCredentials(userService.subscribeStateFor(service))" mat-raised-button class="mat-elevation-z6"> Credentials </button>
</td>
<td *ngIf="stateItem.state === 'failed'">
<button (click)="dialog.openMessage(stateItem)" mat-raised-button class="mat-elevation-z6"> Failure </button>
</td>
<td *ngIf="stateItem.state === 'rejected'">
<button (click)="dialog.openMessage(stateItem)" mat-raised-button class="mat-elevation-z6"> Rejected </button>
</td>
<td *ngIf="!stateItem.is_pending && stateItem.is_credential_pending">
<span class="spaced">
<mat-progress-spinner diameter="24" mode="indeterminate"></mat-progress-spinner>
</span>
SSH Keys pending
</td>
</ng-container>
<ng-template #noStateItem>
<td>
<span>
<mat-icon mat-icon-button matTooltip="Access to this service was never requested.">call_received</mat-icon>
</span>
{{ lang.printState('not_deployed') }}
</td>
</ng-template>
</tr>
</tbody>
</table>
</div>
<ng-template #noDeployment>
<p>
Your credentials were never deployed to the service {{ service.name }}
</p>
</ng-template>
<mat-action-row>
<div [ngSwitch]="(deployment$ | async)?.state_target">
<span *ngSwitchCase="'not_deployed'" style="margin-right: 15px;">
You did not request access to this service.
</span>
<span *ngSwitchCase="'deployed'" style="margin-right: 15px;">
You requested access to this service.
</span>
</div>
<div [ngSwitch]="(deployment$ | async)?.state_target">
<button *ngSwitchCase="'not_deployed'" (click)="changeDeployment('add')" mat-raised-button color="primary" matTooltip="Deploy credentials the service {{ service.name }}">
Deploy my credentials
</button>
<button *ngSwitchCase="'deployed'" (click)="changeDeployment('remove')" mat-raised-button color="primary" matTooltip="Remove credentials from the service {{ service.name }}">
Remove my credentials
</button>
<button *ngSwitchDefault (click)="changeDeployment('add')" mat-raised-button color="primary" matTooltip="Deploy credentials to the service {{ service.name }} the first time">
Deploy my credentials
</button>
</div>
</mat-action-row>
</mat-expansion-panel>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ServiceComponent } from './service.component';
describe('ServiceComponent', () => {
let component: ServiceComponent;
let fixture: ComponentFixture<ServiceComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ServiceComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ServiceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { Observable } from 'rxjs';
import { UserService } from '../user.service';
import { LanguageService } from '../language.service';
import { DialogService } from '../dialogues/dialog.service';
import { Service, Site, Deployment, DeploymentState } from '../types/types.module';
@Component({
selector: 'app-service',
templateUrl: './service.component.html',
styleUrls: ['./service.component.css']
})
export class ServiceComponent implements OnInit {
@Input() service: Service;
public site: Site;
public deployment$: Observable<Deployment>;
public deploymentState$: Observable<DeploymentState>;
constructor(
public userService: UserService,
public lang: LanguageService,
public dialog: DialogService,
) { }
ngOnInit() {
this.site = this.service.site;
this.deploymentState$ = this.userService.subscribeStateFor(this.service);
this.deployment$ = this.userService.subscribeDeployment(
(dep: Deployment) => dep.service ? dep.service.id == this.service.id : false
);
}
ngOnDestroy() {
}
changeDeployment(action: string): void {
this.userService.changeServiceDeployment(action, this.service);
}
}
...@@ -311,7 +311,7 @@ export class UserService { ...@@ -311,7 +311,7 @@ export class UserService {
); );
} }
public changeDeployment(action: string, vo: VO): void { public changeVODeployment(action: string, vo: VO): void {
const body = { const body = {
'type': action, 'type': action,
'vo': vo.id, 'vo': vo.id,
...@@ -323,6 +323,18 @@ export class UserService { ...@@ -323,6 +323,18 @@ export class UserService {
); );
} }
public changeServiceDeployment(action: string, service: Service): void {
const body = {
'type': action,
'service': service.id,
};
this.http.post<Deployment>('/backend/api/deployments', body).pipe(
catchError(this.catchError(true, "Error changing deployment")),
).subscribe(
(dep: Deployment) => this.updateDeployment(dep),
);
}
// DATA SERVICE API // DATA SERVICE API
// //
public subscribeSpecific<T>(selector: (user: User) => T): Observable<T> { public subscribeSpecific<T>(selector: (user: User) => T): Observable<T> {
...@@ -345,10 +357,10 @@ export class UserService { ...@@ -345,10 +357,10 @@ export class UserService {
return this.deploymentStates$.asObservable(); return this.deploymentStates$.asObservable();
} }
public subscribeStateFor(site: Site, service: Service): Observable<DeploymentState> { public subscribeStateFor(service: Service): Observable<DeploymentState> {
return this.deploymentStates$.asObservable().pipe( return this.deploymentStates$.asObservable().pipe(
map((states: DeploymentState[]) => states.find( map((states: DeploymentState[]) => states.find(
(dsi: DeploymentState) => dsi.site.id == site.id && dsi.service.id == service.id, (dsi: DeploymentState) => dsi.service.id == service.id,
)), )),
); );
} }
...@@ -358,25 +370,18 @@ export class UserService { ...@@ -358,25 +370,18 @@ export class UserService {
return this.subscribeSpecific<VO[]>(voSelector); return this.subscribeSpecific<VO[]>(voSelector);
} }
public subscribeServiceDeployment(service: Service): Observable<Deployment> { public subscribeDeployment(selector: (dep: Deployment) => boolean): Observable<Deployment> {
return this.subscribeDeployments().pipe( return this.subscribeDeployments().pipe(
map((deployments: Deployment[]) => { map((deployments: Deployment[]) => {
return deployments.find( return deployments.find(
(dep: Deployment) => dep.service ? dep.service.id == service.id : false (dep: Deployment) => selector(dep),
); );
} }
), ),
); );
} }
public subscribeVODeployment(vo: VO): Observable<Deployment> { public subscribeServices(): Observable<Service[]> {
return this.subscribeDeployments().pipe( return this.subscribeSpecific(this.serviceSelector);
map((deployments: Deployment[]) => {
return deployments.find(
(dep: Deployment) => dep.vo ? dep.vo.id == vo.id : false
);
}
),
);
} }
} }
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title style="min-width: 100px;">{{ vo.pretty_name }}</mat-panel-title> <mat-panel-title style="min-width: 100px;">{{ vo.pretty_name }}</mat-panel-title>
<mat-panel-description>{{ vo.description }}</mat-panel-description> <mat-panel-description *ngIf="(deployment$ | async) as dep">
<span *ngIf="dep.state_target == 'deployed'">
Access requested
</span>
</mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<!-- expansion body --> <!-- expansion body -->
<p *ngIf="vo.description != ''">
{{ vo.description }}
</p>
<div *ngIf="(services$$ | async) as services; else noServiceList"> <div *ngIf="(services$$ | async) as services; else noServiceList">
<div *ngIf="services != undefined && services.length > 0; else noServiceList"> <div *ngIf="services != undefined && services.length > 0; else noServiceList">
<p> <p>
...@@ -33,7 +40,7 @@ ...@@ -33,7 +40,7 @@
{{ service.name }} {{ service.name }}
</span> </span>
</td> </td>
<ng-container *ngIf="(subscribeStateItem(site, service) | async) as stateItem; else noStateItem"> <ng-container *ngIf="(userService.subscribeStateFor(service) | async) as stateItem; else noStateItem">
<td> <td>
<span [ngSwitch]="stateItem.state" class="spaced"> <span [ngSwitch]="stateItem.state" class="spaced">
<mat-icon *ngSwitchCase="'deployed'" matTooltip="The credentials are deployed for the service {{ service.name }}. Click to see details.">call_made</mat-icon> <mat-icon *ngSwitchCase="'deployed'" matTooltip="The credentials are deployed for the service {{ service.name }}. Click to see details.">call_made</mat-icon>
...@@ -51,7 +58,7 @@ ...@@ -51,7 +58,7 @@
<button (click)="dialog.openQuestionnaire(stateItem)" mat-raised-button class="mat-elevation-z6"> Questionnaire </button> <button (click)="dialog.openQuestionnaire(stateItem)" mat-raised-button class="mat-elevation-z6"> Questionnaire </button>
</td> </td>
<td *ngIf="stateItem.state === 'deployed'"> <td *ngIf="stateItem.state === 'deployed'">
<button (click)="dialog.openCredentials(subscribeStateItem(site, service))" mat-raised-button class="mat-elevation-z6"> Credentials </button> <button (click)="dialog.openCredentials(userService.subscribeStateFor(service))" mat-raised-button class="mat-elevation-z6"> Credentials </button>
</td> </td>
<td *ngIf="stateItem.state === 'failed'"> <td *ngIf="stateItem.state === 'failed'">
<button (click)="dialog.openMessage(stateItem)" mat-raised-button class="mat-elevation-z6"> Failure </button> <button (click)="dialog.openMessage(stateItem)" mat-raised-button class="mat-elevation-z6"> Failure </button>
...@@ -81,7 +88,7 @@ ...@@ -81,7 +88,7 @@
</div> </div>
</div> </div>
<ng-template #noServiceList> <ng-template #noServiceList>
<div *ngIf="(deployment$$ | async) as deployment; else noDeployment"> <div *ngIf="(deployment$ | async) as deployment; else noDeployment">
<p *ngIf="deployment.state_target === 'deployed'; else noDeployment"> <p *ngIf="deployment.state_target === 'deployed'; else noDeployment">
Currently, no services need membership of this VO. We will deploy your credentials to new services. Currently, no services need membership of this VO. We will deploy your credentials to new services.
</p> </p>
...@@ -93,7 +100,7 @@ ...@@ -93,7 +100,7 @@
</p> </p>
</ng-template> </ng-template>
<mat-action-row> <mat-action-row>
<div [ngSwitch]="(deployment$$ | async)?.state_target"> <div [ngSwitch]="(deployment$ | async)?.state_target">
<span *ngSwitchCase="'not_deployed'" style="margin-right: 15px;"> <span *ngSwitchCase="'not_deployed'" style="margin-right: 15px;">
You did not request access to services of this VO. You did not request access to services of this VO.
</span> </span>
...@@ -101,7 +108,7 @@ ...@@ -101,7 +108,7 @@
You requested access to the services of this VO. You requested access to the services of this VO.
</span> </span>
</div> </div>
<div [ngSwitch]="(deployment$$ | async)?.state_target"> <div [ngSwitch]="(deployment$ | async)?.state_target">
<button *ngSwitchCase="'not_deployed'" (click)="changeDeployment('add')" mat-raised-button color="primary" matTooltip="Deploy credentials to services of VO {{ vo.name }}"> <button *ngSwitchCase="'not_deployed'" (click)="changeDeployment('add')" mat-raised-button color="primary" matTooltip="Deploy credentials to services of VO {{ vo.name }}">
Deploy my credentials Deploy my credentials
</button> </button>
......
...@@ -25,7 +25,7 @@ export class VoDataComponent implements OnInit { ...@@ -25,7 +25,7 @@ export class VoDataComponent implements OnInit {
private sites$ = new BehaviorSubject<Site[]>([]); private sites$ = new BehaviorSubject<Site[]>([]);
public sites$$: Observable<Site[]>; public sites$$: Observable<Site[]>;
public deployment$$: Observable<Deployment>; public deployment$: Observable<Deployment>;
constructor( constructor(
public userService: UserService, public userService: UserService,
...@@ -35,7 +35,7 @@ export class VoDataComponent implements OnInit { ...@@ -35,7 +35,7 @@ export class VoDataComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.userService.subscribeSpecific(this.userService.serviceSelector).subscribe( this.userService.subscribeServices().subscribe(
(services: Service[]) => { (services: Service[]) => {
// filter by the vo of this vo // filter by the vo of this vo
this.services = services.filter( this.services = services.filter(
...@@ -60,7 +60,9 @@ export class VoDataComponent implements OnInit { ...@@ -60,7 +60,9 @@ export class VoDataComponent implements OnInit {
this.services$$ = this.services$.asObservable(); this.services$$ = this.services$.asObservable();
this.sites$$ = this.sites$.asObservable(); this.sites$$ = this.sites$.asObservable();
this.deployment$$ = this.userService.subscribeVODeployment(this.vo); this.deployment$ = this.userService.subscribeDeployment(
(dep: Deployment) => dep.vo ? dep.vo.id == this.vo.id : false
);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
...@@ -78,11 +80,7 @@ export class VoDataComponent implements OnInit { ...@@ -78,11 +80,7 @@ export class VoDataComponent implements OnInit {
); );
} }
public subscribeStateItem(site: Site, service: Service): Observable<DeploymentState> {
return this.userService.subscribeStateFor(site, service);
}
public changeDeployment(action: string): void { public changeDeployment(action: string): void {
this.userService.changeDeployment(action, this.vo); this.userService.changeVODeployment(action, this.vo);
} }
} }
...@@ -97,3 +97,7 @@ mat-checkbox { ...@@ -97,3 +97,7 @@ mat-checkbox {
height: unset; height: unset;
min-width: 800px; min-width: 800px;
} }
mat-panel-description > * {
margin: 0px 16px 0px auto;
}
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