feat(console): refresh roles, user-grants and project-members based on previous navigation (#728)

* feat: refresh roles, user-grants, project-members

* refresh keys

* user grants on granted projects

* refresh on member changes

* membership refresh

* lint
This commit is contained in:
Max Peintner 2020-09-11 14:24:41 +02:00 committed by GitHub
parent 15c977dab4
commit 4926509de0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 134 additions and 13 deletions

View File

@ -38,6 +38,7 @@ import { GrpcService } from './services/grpc.service';
import { AuthInterceptor } from './services/interceptors/auth.interceptor';
import { GRPC_INTERCEPTORS } from './services/interceptors/grpc-interceptor';
import { OrgInterceptor } from './services/interceptors/org.interceptor';
import { RefreshService } from './services/refresh.service';
import { StatehandlerProcessorService, StatehandlerProcessorServiceImpl } from './services/statehandler-processor.service';
import { StatehandlerService, StatehandlerServiceImpl } from './services/statehandler.service';
import { StorageService } from './services/storage.service';
@ -154,6 +155,7 @@ const authConfig: AuthConfig = {
multi: true,
useClass: OrgInterceptor,
},
RefreshService,
GrpcService,
GrpcAuthService,
{ provide: 'windowObject', useValue: window },

View File

@ -103,7 +103,11 @@ export class ProjectMembersComponent {
this.toast.showError(error);
});
}
}));
})).then(() => {
setTimeout(() => {
this.changePage();
}, 1000);
});
}
public isAllSelected(): boolean {
@ -141,6 +145,9 @@ export class ProjectMembersComponent {
user.id, roles);
}
})).then(() => {
setTimeout(() => {
this.changePage();
}, 1000);
this.toast.showInfo('PROJECT.TOAST.MEMBERSADDED', true);
}).catch(error => {
this.toast.showError(error);

View File

@ -1,5 +1,6 @@
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult"
[selection]="selection" [loading]="dataSource?.loading$ | async" [timestamp]="dataSource?.viewTimestamp">
emitRefreshOnPreviousRoute="/projects/{{projectId}}/roles/create" [selection]="selection"
[loading]="dataSource?.loading$ | async" [timestamp]="dataSource?.viewTimestamp">
<ng-template appHasRole [appHasRole]="['project.role.delete', 'project.role.delete:' + projectId]" actions>
<button color="warn" class="icon-button" [disabled]="disabled"
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button

View File

@ -2,6 +2,7 @@ import { animate, animation, keyframes, style, transition, trigger, useAnimation
import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { RefreshService } from 'src/app/services/refresh.service';
const rotate = animation([
animate(
@ -32,14 +33,24 @@ export class RefreshTableComponent implements OnInit {
@Input() public dataSize: number = 0;
@Input() public emitRefreshAfterTimeoutInMs: number = 0;
@Input() public loading: boolean = false;
@Input() public emitRefreshOnPreviousRoute: string = '';
@Output() public refreshed: EventEmitter<void> = new EventEmitter();
constructor(private refreshService: RefreshService) { }
ngOnInit(): void {
if (this.emitRefreshAfterTimeoutInMs) {
setTimeout(() => {
this.emitRefresh();
}, this.emitRefreshAfterTimeoutInMs);
}
if (this.emitRefreshOnPreviousRoute && this.refreshService.previousUrls.includes(this.emitRefreshOnPreviousRoute)) {
setTimeout(() => {
console.log('refresh now');
this.emitRefresh();
}, 1000);
}
}
emitRefresh(): void {

View File

@ -1,5 +1,6 @@
<app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
[timestamp]="dataSource?.viewTimestamp" [dataSize]="dataSource?.totalResult" [selection]="selection">
[emitRefreshOnPreviousRoute]="refreshOnPreviousRoute" [timestamp]="dataSource?.viewTimestamp"
[dataSize]="dataSource?.totalResult" [selection]="selection">
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && allowDelete">
<i class="las la-trash"></i>
@ -21,9 +22,8 @@
<td class="selection" mat-cell *matCellDef="let row">
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
<app-avatar *ngIf="row && (row?.displayName || (row.firstName && row.lastName))" class="avatar"
[name]="row.displayName ? row.displayName : (row.firstName + ' '+ row.lastName)"
[size]="32">
<app-avatar *ngIf="row && row?.displayName && row.firstName && row.lastName" class="avatar"
[name]="row.displayName" [size]="32">
</app-avatar>
</mat-checkbox>
</td>
@ -33,7 +33,7 @@
<ng-container matColumnDef="user">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let grant">
{{grant?.firstName}} {{grant?.lastName}}</td>
{{grant?.displayName}}</td>
</ng-container>
<ng-container matColumnDef="org">

View File

@ -24,6 +24,7 @@ import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource
})
export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() context: UserGrantContext = UserGrantContext.USER;
@Input() refreshOnPreviousRoute: string = '';
public grants: UserGrantView.AsObject[] = [];
public dataSource!: UserGrantsDataSource;

View File

@ -85,6 +85,9 @@ export class IamMembersComponent implements AfterViewInit {
public removeMember(member: ProjectMember.AsObject): void {
this.adminService.RemoveIamMember(member.userId).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -120,6 +123,9 @@ export class IamMembersComponent implements AfterViewInit {
return this.adminService.AddIamMember(user.id, roles);
})).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -57,6 +57,9 @@ export class IamComponent {
return this.adminService.AddIamMember(user.id, roles);
})).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERADDED');
setTimeout(() => {
this.loadMembers();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -180,6 +180,9 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
return this.mgmtService.AddMyOrgMember(user.id, roles);
})).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadMembers();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -82,7 +82,11 @@ export class OrgMembersComponent implements AfterViewInit {
}).catch(error => {
this.toast.showError(error);
});
}));
})).then(() => {
setTimeout(() => {
this.refreshPage();
}, 1000);
});
}
public isAllSelected(): boolean {
@ -115,6 +119,9 @@ export class OrgMembersComponent implements AfterViewInit {
return this.mgmtService.AddMyOrgMember(user.id, roles);
})).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -19,7 +19,8 @@
[projectId]="projectId" [grantId]="grantId"
[displayedColumns]="['select','user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
[allowCreate]="['user.grant.write','user.grant.write:'+grantId] | hasRole | async"
[allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async">
[allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async"
refreshOnPreviousRoute="/grant-create/project/{{projectId}}/grant/{{grantId}}">
</app-user-grants>
</app-card>
</ng-template>

View File

@ -152,6 +152,9 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
roles,
).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadMembers();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -68,7 +68,7 @@
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants
<app-project-grants refreshOnPreviousRoute="/projects/{{projectId}}/grants/create"
[disabled]="((['project.grant.write', 'project.grant.write:'+ project.projectId]| hasRole | async) && (['org.global.read']| hasRole | async))== false"
[projectId]="projectId">
</app-project-grants>
@ -89,6 +89,7 @@
<app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
refreshOnPreviousRoute="/grant-create/project/{{projectId}}"
[allowCreate]="(['user.grant.write', 'user.grant.write:'+projectId] | hasRole) | async"
[allowDelete]="(['user.grant.delete','user.grant.delete:'+projectId] | hasRole) | async">
</app-user-grants>

View File

@ -226,6 +226,9 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
return this.mgmtService.AddProjectMember(this.projectId, user.id, roles)
.then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadMembers();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -1,5 +1,6 @@
<app-refresh-table [loading]="dataSource?.loading$ | async" *ngIf="projectId" (refreshed)="loadGrantsPage()"
[dataSize]="dataSource.totalResult" [selection]="selection" [timestamp]="dataSource?.viewTimestamp">
[dataSize]="dataSource.totalResult" [selection]="selection" [timestamp]="dataSource?.viewTimestamp"
[emitRefreshOnPreviousRoute]="refreshOnPreviousRoute">
<ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']"
actions>
<button (click)="deleteSelectedGrants()" [disabled]="disabled" mat-icon-button *ngIf="selection.hasValue()"

View File

@ -24,6 +24,7 @@ import { ProjectGrantsDataSource } from './project-grants-datasource';
],
})
export class ProjectGrantsComponent implements OnInit, AfterViewInit {
@Input() refreshOnPreviousRoute: string = '';
@Input() public projectId: string = '';
@Input() public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;

View File

@ -100,7 +100,10 @@ export class MachineKeysComponent implements OnInit {
console.log(this.userId, type, date);
return this.userService.AddMachineKey(this.userId, type, date).then((response) => {
if (response) {
console.log(response.toObject());
setTimeout(() => {
this.refreshPage();
}, 1000);
this.dialog.open(ShowKeyDialogComponent, {
data: {
key: response.toObject(),

View File

@ -132,6 +132,9 @@ export class MembershipDetailComponent implements AfterViewInit {
return this.adminService.AddIamMember(user.id, roles);
})).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -147,6 +150,9 @@ export class MembershipDetailComponent implements AfterViewInit {
return this.mgmtService.AddMyOrgMember(user.id, roles);
})).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -166,6 +172,9 @@ export class MembershipDetailComponent implements AfterViewInit {
roles,
).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -182,6 +191,9 @@ export class MembershipDetailComponent implements AfterViewInit {
return this.mgmtService.AddProjectMember(response.projectId, user.id, roles)
.then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -32,6 +32,11 @@
aria-label="add membership">
<mat-icon>add</mat-icon>
</button>
<span class="fill-space"></span>
<button class="refresh-img" (click)="loadManager(user.id)" [disabled]="disabled" mat-icon-button
aria-label="refresh contributors">
<mat-icon>refresh</mat-icon>
</button>
</div>
</div>
</div>

View File

@ -48,6 +48,15 @@
margin: 0 8px 0 -15px;
}
.fill-space {
flex: 1;
}
.refresh-img {
float: left;
margin: 0 8px 0 -15px;
}
.avatar-circle {
float: left;
margin: 0 8px 0 -12px;

View File

@ -100,6 +100,9 @@ export class MembershipsComponent implements OnInit {
return this.adminService.AddIamMember(user.id, roles);
})).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadManager(this.user.id);
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -115,6 +118,9 @@ export class MembershipsComponent implements OnInit {
return this.mgmtService.AddMyOrgMember(user.id, roles);
})).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadManager(this.user.id);
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -134,6 +140,9 @@ export class MembershipsComponent implements OnInit {
roles,
).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadManager(this.user.id);
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
@ -150,6 +159,9 @@ export class MembershipsComponent implements OnInit {
return this.mgmtService.AddProjectMember(response.projectId, user.id, roles)
.then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => {
this.loadManager(this.user.id);
}, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -1,5 +1,6 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[timestamp]="userResult?.viewTimestamp" [selection]="selection">
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
[emitRefreshOnPreviousRoute]="refreshOnPreviousRoute">
<ng-template appHasRole [appHasRole]="['user.write']" actions>
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">

View File

@ -19,6 +19,7 @@ import { UserType } from '../user-list.component';
export class UserTableComponent implements OnInit {
public UserType: any = UserType;
@Input() userType: UserType = UserType.HUMAN;
@Input() refreshOnPreviousRoute: string = '';
@Input() disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
public dataSource: MatTableDataSource<UserView.AsObject> = new MatTableDataSource<UserView.AsObject>();

View File

@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { Event, NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class RefreshService {
private fifo: Array<string> = ['', ''];
constructor(router: Router) {
router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: Event | any) => {
console.log('prev:', event.url);
this.moveInto(event.url);
});
}
public get previousUrls(): Array<string> {
return this.fifo;
}
private moveInto(value: string): void {
this.fifo[1] = this.fifo[0];
this.fifo[0] = value;
}
}