From f40f10de3950b9115ac0d74e48d95e3c031a411b Mon Sep 17 00:00:00 2001 From: Alexander Kharkevich Date: Tue, 9 Apr 2024 22:40:28 -0400 Subject: [PATCH] feat: ui integration (#10) Co-authored-by: Aleksei Moiseev --- ...experiment-permission-details.component.ts | 56 ++++----- .../model-permission-details.component.html | 2 +- .../model-permission-details.component.ts | 39 ++----- .../user-permission-details.component.html | 4 + .../user-permission-details.component.ts | 106 +++++++++++++++--- .../user-permission-details.config.ts | 12 ++ .../services/permission-modal.service.ts | 79 +++++++++++++ .../specs/permission-modal.service.spec.ts | 16 +++ 8 files changed, 235 insertions(+), 79 deletions(-) create mode 100644 web-ui/src/app/shared/services/permission-modal.service.ts create mode 100644 web-ui/src/app/shared/services/specs/permission-modal.service.spec.ts diff --git a/web-ui/src/app/features/admin-page/components/details/experiment-permission-details/experiment-permission-details.component.ts b/web-ui/src/app/features/admin-page/components/details/experiment-permission-details/experiment-permission-details.component.ts index c718423..33c626c 100644 --- a/web-ui/src/app/features/admin-page/components/details/experiment-permission-details/experiment-permission-details.component.ts +++ b/web-ui/src/app/features/admin-page/components/details/experiment-permission-details/experiment-permission-details.component.ts @@ -2,21 +2,19 @@ import { Component, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute } from '@angular/router'; +import { GrantUserPermissionsComponent, GrantUserPermissionsModel } from 'src/app/shared/components'; import { - EditPermissionsModalComponent, - GrantUserPermissionsComponent, - GrantUserPermissionsModel, -} from 'src/app/shared/components'; -import { ExperimentsDataService, PermissionDataService, UserDataService } from 'src/app/shared/services'; + ExperimentsDataService, + PermissionDataService, + SnackBarService, + UserDataService, +} from 'src/app/shared/services'; import { TableActionEvent, TableActionModel } from 'src/app/shared/components/table/table.interface'; -import { filter, switchMap } from 'rxjs'; +import { filter, switchMap, tap } from 'rxjs'; import { TableActionEnum } from 'src/app/shared/components/table/table.config'; -import { EntityEnum } from 'src/app/core/configs/core'; import { COLUMN_CONFIG, TABLE_ACTIONS } from './experiment-permission-details.config'; -import { - PermissionsDialogData, -} from 'src/app/shared/components/modals/edit-permissions-modal/edit-permissions-modal.interface'; import { PermissionEnum } from 'src/app/core/configs/permissions'; +import { PermissionModalService } from '../../../../../shared/services/permission-modal.service'; @Component({ selector: 'ml-experiment-permission-details', @@ -35,6 +33,8 @@ export class ExperimentPermissionDetailsComponent implements OnInit { private readonly permissionDataService: PermissionDataService, private readonly userDataService: UserDataService, private readonly route: ActivatedRoute, + private readonly permissionModalService: PermissionModalService, + private readonly snackBarService: SnackBarService, ) { } ngOnInit(): void { @@ -47,27 +47,15 @@ export class ExperimentPermissionDetailsComponent implements OnInit { }); } - handleUserEdit(event: { permission: string; username: string }) { - const data: PermissionsDialogData = { - userName: event.username, - entityName: this.experimentId, - entityType: EntityEnum.EXPERIMENT, - permission: event.permission as PermissionEnum, - }; - - this.dialog - .open(EditPermissionsModalComponent, { data }) - .afterClosed() + handleUserEdit(event: { permission: PermissionEnum; username: string }) { + this.permissionModalService.openEditUserPermissionsForExperimentModal(this.experimentId, event.username, event.permission) .pipe( - switchMap((data) => this.permissionDataService.updateExperimentPermission({ - user_name: event.username, - experiment_id: this.experimentId, - new_permission: data.permission, - })) + tap(() => this.snackBarService.openSnackBar('Permissions updated successfully')), + switchMap(() => this.experimentDataService.getUsersForExperiment(this.experimentId)), ) - .subscribe((data) => { - console.log(data) - }) + .subscribe((users) => { + this.userDataSource = users; + }); } handleActions($event: TableActionEvent<{ permission: string; username: string }>) { @@ -85,7 +73,11 @@ export class ExperimentPermissionDetailsComponent implements OnInit { revokePermissionForUser(item: any) { this.permissionDataService.deleteExperimentPermission( { experiment_id: this.experimentId, user_name: item.username }) - .subscribe(console.log); + .pipe( + tap(() => this.snackBarService.openSnackBar('Permission revoked successfully')), + switchMap(() => this.loadUsersForExperiment(this.experimentId)), + ) + .subscribe((users) => this.userDataSource = users); } addUser() { @@ -102,9 +94,7 @@ export class ExperimentPermissionDetailsComponent implements OnInit { })), switchMap(() => this.loadUsersForExperiment(this.experimentId)), ) - .subscribe((users) => { - this.userDataSource = users; - }); + .subscribe((users) => this.userDataSource = users); } loadUsersForExperiment(experimentId: string) { diff --git a/web-ui/src/app/features/admin-page/components/details/model-permission-details/model-permission-details.component.html b/web-ui/src/app/features/admin-page/components/details/model-permission-details/model-permission-details.component.html index 697ad33..038d405 100644 --- a/web-ui/src/app/features/admin-page/components/details/model-permission-details/model-permission-details.component.html +++ b/web-ui/src/app/features/admin-page/components/details/model-permission-details/model-permission-details.component.html @@ -1,6 +1,6 @@
-

Experiment Access

+

Model Access

@@ -31,6 +33,8 @@ [columnConfig]="modelsColumnConfig" [data]="modelsDataSource" *ngIf="modelsDataSource.length" + [actions]="modelsActions" + (action)="handleModelActions($event)" >
diff --git a/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.component.ts b/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.component.ts index 6295254..5ef8557 100644 --- a/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.component.ts +++ b/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.component.ts @@ -1,15 +1,28 @@ import { Component, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { filter, forkJoin, switchMap } from 'rxjs'; +import { filter, forkJoin, switchMap, tap } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; import { GrantPermissionModalComponent } from 'src/app/shared/components'; -import { ExperimentsDataService, ModelsDataService, PermissionDataService } from 'src/app/shared/services'; -import { EXPERIMENT_COLUMN_CONFIG, MODEL_COLUMN_CONFIG } from './user-permission-details.config'; +import { + ExperimentsDataService, + ModelsDataService, + PermissionDataService, + SnackBarService, +} from 'src/app/shared/services'; +import { + EXPERIMENT_ACTIONS, + EXPERIMENT_COLUMN_CONFIG, + MODEL_ACTIONS, + MODEL_COLUMN_CONFIG, +} from './user-permission-details.config'; import { EntityEnum } from 'src/app/core/configs/core'; import { GrantPermissionModalData, } from 'src/app/shared/components/modals/grant-permissoin-modal/grant-permission-modal.inteface'; +import { TableActionEvent, TableActionModel } from 'src/app/shared/components/table/table.interface'; +import { PermissionModalService } from 'src/app/shared/services/permission-modal.service'; +import { TableActionEnum } from 'src/app/shared/components/table/table.config'; @Component({ selector: 'ml-user-permission-details', @@ -23,6 +36,8 @@ export class UserPermissionDetailsComponent implements OnInit { experimentsDataSource: any[] = []; modelsDataSource: any[] = []; + experimentsActions: TableActionModel[] = EXPERIMENT_ACTIONS; + modelsActions: TableActionModel[] = MODEL_ACTIONS; constructor( private readonly dialog: MatDialog, @@ -30,6 +45,8 @@ export class UserPermissionDetailsComponent implements OnInit { private readonly modelDataService: ModelsDataService, private readonly permissionDataService: PermissionDataService, private readonly route: ActivatedRoute, + private readonly permissionModalService: PermissionModalService, + private readonly snackBarService: SnackBarService ) { } @@ -48,24 +65,18 @@ export class UserPermissionDetailsComponent implements OnInit { } addModelPermissionToUser() { - this.modelDataService.getAllModels() + this.permissionModalService.openGrantModelPermissionModal(this.userId) .pipe( - switchMap((models) => this.dialog.open(GrantPermissionModalComponent, { - data: { - entityType: EntityEnum.MODEL, - entities: models.map(({ name }) => name), - userName: this.userId, - } - }).afterClosed() - ), filter(Boolean), - switchMap(({ entity, permission, user }) => this.permissionDataService.createModelPermission({ + switchMap(({ entity, permission }) => this.permissionDataService.createModelPermission({ user_name: this.userId, model_name: entity, new_permission: permission, })), + tap(() => this.snackBarService.openSnackBar('Permission granted successfully')), + switchMap(() => this.modelDataService.getModelsForUser(this.userId)), ) - .subscribe(); + .subscribe((models) => this.modelsDataSource = models); } addExperimentPermissionToUser() { @@ -80,14 +91,77 @@ export class UserPermissionDetailsComponent implements OnInit { }).afterClosed() ), filter(Boolean), - switchMap(({ entity, permission, user }) => { + switchMap(({ entity, permission }) => { return this.permissionDataService.createExperimentPermission({ user_name: this.userId, experiment_name: entity, new_permission: permission, }) }), + tap(() => this.snackBarService.openSnackBar('Permission granted successfully')), + switchMap(() => this.expDataService.getExperimentsForUser(this.userId)), + ) + .subscribe((experiments) => this.experimentsDataSource = experiments); + } + + handleExperimentActions(event: TableActionEvent) { + const actionMapping: { [key: string]: any } = { + [TableActionEnum.EDIT]: this.handleEditUserPermissionForExperiment.bind(this), + [TableActionEnum.REVOKE]: this.revokeExperimentPermissionForUser.bind(this), + } + + const selectedAction = actionMapping[event.action.action]; + if (selectedAction) { + selectedAction(event.item); + } + } + + revokeExperimentPermissionForUser(item: {id: string}) { + this.permissionDataService.deleteExperimentPermission({experiment_id: item.id, user_name: this.userId}) + .pipe( + tap(() => this.snackBarService.openSnackBar('Permission revoked successfully')), + switchMap(() => this.expDataService.getExperimentsForUser(this.userId)), + ) + .subscribe((experiments) => this.experimentsDataSource = experiments); + + } + + revokeModelPermissionForUser({name}: any) { + this.permissionDataService.deleteModelPermission({model_name: name, user_name: this.userId}) + .pipe( + tap(() => this.snackBarService.openSnackBar('Permission revoked successfully')), + switchMap(() => this.modelDataService.getModelsForUser(this.userId)), + ) + .subscribe((models) => this.modelsDataSource = models); + } + + handleModelActions({ action, item }: TableActionEvent) { + const actionMapping: { [key: string]: any } = { + [TableActionEnum.EDIT]: this.handleEditUserPermissionForModel.bind(this), + [TableActionEnum.REVOKE]: this.revokeModelPermissionForUser.bind(this), + } + + const selectedAction = actionMapping[action.action]; + if (selectedAction) { + selectedAction(item); + } + } + + handleEditUserPermissionForModel({ name, permissions }: any) { + this.permissionModalService.openEditUserPermissionsForModelModal(name, this.userId, permissions) + .pipe( + tap(() => this.snackBarService.openSnackBar('Permissions updated successfully')), + switchMap(() => this.modelDataService.getModelsForUser(this.userId)), + ) + .subscribe((models) => this.modelsDataSource = models); + } + + handleEditUserPermissionForExperiment({ id, permissions }: any) { + this.permissionModalService.openEditUserPermissionsForExperimentModal(id, this.userId, permissions) + .pipe( + tap(() => this.snackBarService.openSnackBar('Permissions updated successfully')), + switchMap(() => this.expDataService.getExperimentsForUser(this.userId)), ) - .subscribe(); + .subscribe((experiments) => this.experimentsDataSource = experiments); } } diff --git a/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.config.ts b/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.config.ts index 8b78df5..6c569f2 100644 --- a/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.config.ts +++ b/web-ui/src/app/features/admin-page/components/details/user-permission-details/user-permission-details.config.ts @@ -1,3 +1,5 @@ +import { TABLE_ACTION_CONFIG } from 'src/app/shared/components/table/table.config'; + export const MODEL_COLUMN_CONFIG = [ { title: 'Model name', key: 'name' }, { title: 'Permissions', key: 'permissions' }, @@ -7,3 +9,13 @@ export const EXPERIMENT_COLUMN_CONFIG = [ { title: 'Experiment Name', key: 'name' }, { title: 'Permission', key: 'permissions' }, ]; + +export const EXPERIMENT_ACTIONS = [ + TABLE_ACTION_CONFIG.EDIT, + TABLE_ACTION_CONFIG.REVOKE, +]; + +export const MODEL_ACTIONS = [ + TABLE_ACTION_CONFIG.EDIT, + TABLE_ACTION_CONFIG.REVOKE, +]; diff --git a/web-ui/src/app/shared/services/permission-modal.service.ts b/web-ui/src/app/shared/services/permission-modal.service.ts new file mode 100644 index 0000000..42fc6c1 --- /dev/null +++ b/web-ui/src/app/shared/services/permission-modal.service.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@angular/core'; +import { EditPermissionsModalComponent, GrantPermissionModalComponent } from '../components'; +import { PermissionsDialogData } from '../components/modals/edit-permissions-modal/edit-permissions-modal.interface'; +import { filter, switchMap } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { EntityEnum } from '../../core/configs/core'; +import { PermissionEnum } from '../../core/configs/permissions'; +import { PermissionDataService } from './data/permission-data.service'; +import { GrantPermissionModalData } from '../components/modals/grant-permissoin-modal/grant-permission-modal.inteface'; +import { ModelsDataService } from './data/models-data.service'; + +@Injectable({ + providedIn: 'root', +}) +export class PermissionModalService { + + constructor( + private readonly dialog: MatDialog, + private readonly permissionDataService: PermissionDataService, + private readonly modelDataService: ModelsDataService, + ) { + } + + openEditUserPermissionsForModelModal(modelName: string, userName: string, currentPermission: PermissionEnum) { + const data: PermissionsDialogData = { + userName: userName, + entityName: modelName, + entityType: EntityEnum.MODEL, + permission: currentPermission, + }; + + return this.dialog + .open(EditPermissionsModalComponent, { data }) + .afterClosed() + .pipe( + filter(Boolean), + switchMap(({ permission }) => this.permissionDataService.updateModelPermission({ + model_name: modelName, + new_permission: permission, + user_name: userName, + })), + ) + } + + openEditUserPermissionsForExperimentModal(experimentName: string, userName: string, currentPermission: PermissionEnum) { + const data: PermissionsDialogData = { + userName: userName, + entityName: experimentName, + entityType: EntityEnum.EXPERIMENT, + permission: currentPermission, + }; + + return this.dialog + .open(EditPermissionsModalComponent, { data }) + .afterClosed() + .pipe( + filter(Boolean), + switchMap(({ permission }) => this.permissionDataService.updateExperimentPermission({ + experiment_id: experimentName, + new_permission: permission, + user_name: userName, + })), + ) + } + + openGrantModelPermissionModal(userName: string) { + return this.modelDataService.getAllModels() + .pipe( + switchMap((models) => this.dialog.open(GrantPermissionModalComponent, { + data: { + entityType: EntityEnum.MODEL, + entities: models.map(({ name }) => name), + userName, + }, + }).afterClosed(), + ), + ) + } +} diff --git a/web-ui/src/app/shared/services/specs/permission-modal.service.spec.ts b/web-ui/src/app/shared/services/specs/permission-modal.service.spec.ts new file mode 100644 index 0000000..40246a1 --- /dev/null +++ b/web-ui/src/app/shared/services/specs/permission-modal.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PermissionModalService } from '../permission-modal.service'; + +describe('PermissionModalService', () => { + let service: PermissionModalService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PermissionModalService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +});