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';
import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component';
import { VoDataComponent } from './vo-data/vo-data.component';
import { ServiceComponent } from './service/service.component';
@NgModule({
......@@ -54,6 +55,7 @@ import { VoDataComponent } from './vo-data/vo-data.component';
BodyComponent,
FooterComponent,
VoDataComponent,
ServiceComponent,
],
providers: [
CookieService,
......
<div class="body" class="mat-typography" class="body" >
<div *ngIf="(userService.subscribeUser() | async) as user" style="max-width: 800px; margin: auto;">
<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>
<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>
</mat-accordion>
<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 {
);
}
public changeDeployment(action: string, vo: VO): void {
public changeVODeployment(action: string, vo: VO): void {
const body = {
'type': action,
'vo': vo.id,
......@@ -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
//
public subscribeSpecific<T>(selector: (user: User) => T): Observable<T> {
......@@ -345,10 +357,10 @@ export class UserService {
return this.deploymentStates$.asObservable();
}
public subscribeStateFor(site: Site, service: Service): Observable<DeploymentState> {
public subscribeStateFor(service: Service): Observable<DeploymentState> {
return this.deploymentStates$.asObservable().pipe(
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 {
return this.subscribeSpecific<VO[]>(voSelector);
}
public subscribeServiceDeployment(service: Service): Observable<Deployment> {
public subscribeDeployment(selector: (dep: Deployment) => boolean): Observable<Deployment> {
return this.subscribeDeployments().pipe(
map((deployments: Deployment[]) => {
return deployments.find(
(dep: Deployment) => dep.service ? dep.service.id == service.id : false
(dep: Deployment) => selector(dep),
);
}
),
);
}
public subscribeVODeployment(vo: VO): Observable<Deployment> {
return this.subscribeDeployments().pipe(
map((deployments: Deployment[]) => {
return deployments.find(
(dep: Deployment) => dep.vo ? dep.vo.id == vo.id : false
);
}
),
);
public subscribeServices(): Observable<Service[]> {
return this.subscribeSpecific(this.serviceSelector);
}
}
<mat-expansion-panel>
<mat-expansion-panel-header>
<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>
<!-- expansion body -->
<p *ngIf="vo.description != ''">
{{ vo.description }}
</p>
<div *ngIf="(services$$ | async) as services; else noServiceList">
<div *ngIf="services != undefined && services.length > 0; else noServiceList">
<p>
......@@ -33,7 +40,7 @@
{{ service.name }}
</span>
</td>
<ng-container *ngIf="(subscribeStateItem(site, service) | async) as stateItem; else noStateItem">
<ng-container *ngIf="(userService.subscribeStateFor(service) | 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>
......@@ -51,7 +58,7 @@
<button (click)="dialog.openQuestionnaire(stateItem)" mat-raised-button class="mat-elevation-z6"> Questionnaire </button>
</td>
<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 *ngIf="stateItem.state === 'failed'">
<button (click)="dialog.openMessage(stateItem)" mat-raised-button class="mat-elevation-z6"> Failure </button>
......@@ -81,7 +88,7 @@
</div>
</div>
<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">
Currently, no services need membership of this VO. We will deploy your credentials to new services.
</p>
......@@ -93,7 +100,7 @@
</p>
</ng-template>
<mat-action-row>
<div [ngSwitch]="(deployment$$ | async)?.state_target">
<div [ngSwitch]="(deployment$ | async)?.state_target">
<span *ngSwitchCase="'not_deployed'" style="margin-right: 15px;">
You did not request access to services of this VO.
</span>
......@@ -101,7 +108,7 @@
You requested access to the services of this VO.
</span>
</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 }}">
Deploy my credentials
</button>
......
......@@ -25,7 +25,7 @@ export class VoDataComponent implements OnInit {
private sites$ = new BehaviorSubject<Site[]>([]);
public sites$$: Observable<Site[]>;
public deployment$$: Observable<Deployment>;
public deployment$: Observable<Deployment>;
constructor(
public userService: UserService,
......@@ -35,7 +35,7 @@ export class VoDataComponent implements OnInit {
}
ngOnInit(): void {
this.userService.subscribeSpecific(this.userService.serviceSelector).subscribe(
this.userService.subscribeServices().subscribe(
(services: Service[]) => {
// filter by the vo of this vo
this.services = services.filter(
......@@ -60,7 +60,9 @@ export class VoDataComponent implements OnInit {
this.services$$ = this.services$.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 {
......@@ -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 {
this.userService.changeDeployment(action, this.vo);
this.userService.changeVODeployment(action, this.vo);
}
}
......@@ -97,3 +97,7 @@ mat-checkbox {
height: unset;
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