Skip to content

Commit

Permalink
feat: ui integration (mlflow-oidc#8)
Browse files Browse the repository at this point in the history
* feat: integration with backendd

* chore: refactoring

* chore: refactoring

* fix: allow use experiment_name and experiment_id simultaneously

* chore: refactoring

* chore: refactoring

* fix: fix CRUD

* fix: fix build script

---------

Co-authored-by: Aleksei Moiseev <[email protected]>
  • Loading branch information
2 people authored and hahahannes committed Oct 14, 2024
1 parent 1e100ff commit 9ce176e
Show file tree
Hide file tree
Showing 102 changed files with 1,332 additions and 754 deletions.
36 changes: 15 additions & 21 deletions mlflow_oidc_auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
_logger = logging.getLogger(__name__)


def _get_experiment_id(request_data: dict) -> str:
experiment_id = request_data.get("experiment_id")
if "experiment_id" not in request_data:
experiment_id = mlflow_client.get_experiment_by_name(request_data.get("experiment_name")).experiment_id
return experiment_id

def _get_request_param(param: str) -> str:
if request.method == "GET":
args = request.args
Expand Down Expand Up @@ -208,16 +214,12 @@ def make_basic_auth_response() -> Response:

def create_experiment_permission():
request_data = request.get_json()
# Get the experiment
experiment = mlflow_client.get_experiment_by_name(request_data.get("experiment_name"))

# # Update the experiment
store.create_experiment_permission(
experiment.experiment_id,
_get_experiment_id(request_data),
request_data.get("user_name"),
request_data.get("new_permission"),
)
return "Experiment permission has been created."
return jsonify({"message": "Experiment permission has been created."})


# Experiment views
Expand Down Expand Up @@ -517,29 +519,21 @@ def _password_generation():

def update_experiment_permission():
request_data = request.get_json()
# Get the experiment
experiment = mlflow_client.get_experiment_by_name(request_data.get("experiment_name"))

# # Update the experiment
store.update_experiment_permission(
experiment.experiment_id,
_get_experiment_id(request_data),
request_data.get("user_name"),
request_data.get("new_permission"),
)
return "Experiment permission has been changed."
return jsonify({"message": "Experiment permission has been changed."})


def delete_experiment_permission():
request_data = request.get_json()
# Get the experiment
experiment = mlflow_client.get_experiment_by_name(request_data.get("experiment_name"))

# # Update the experiment
store.delete_experiment_permission(
experiment.experiment_id,
_get_experiment_id(request_data),
request_data.get("user_name"),
)
return "Experiment permission has been deleted."
return jsonify({"message": "Experiment permission has been deleted."})


def create_model_permission():
Expand All @@ -550,7 +544,7 @@ def create_model_permission():
request_data.get("user_name"),
request_data.get("new_permission"),
)
return "Model permission has been created."
return jsonify({"message": "Model permission has been created."})


def get_model_permission():
Expand All @@ -571,7 +565,7 @@ def update_model_permission():
request_data.get("user_name"),
request_data.get("new_permission"),
)
return "Model permission has been changed."
return jsonify({"message": "Model permission has been changed."})


def delete_model_permission():
Expand All @@ -581,4 +575,4 @@ def delete_model_permission():
request_data.get("model_name"),
request_data.get("user_name"),
)
return "Model permission has been deleted."
return jsonify({"message": "Model permission has been deleted."})
2 changes: 1 addition & 1 deletion web-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"build": "ng build --base-href .",
"clean": "rimraf ../mlflow_oidc_auth/ui",
"release": "yarn clean && ng build --base-href .",
"watch": "ng build --watch --base-href . --configuration development",
"watch": "yarn clean && ng build --watch --base-href . --configuration development",
"test": "ng test"
},
"private": true,
Expand Down
2 changes: 1 addition & 1 deletion web-ui/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const routes: Routes = [
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule],
})
export class AppRoutingModule {}
19 changes: 15 additions & 4 deletions web-ui/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
<div class="container">
<ml-header [name]="name"></ml-header>
<router-outlet> </router-outlet>
</div>
<ng-container *ngIf="!loading && this.user; else loader">

<div class="container">
<ml-header [name]="user.display_name"></ml-header>
<router-outlet> </router-outlet>
</div>

</ng-container>


<ng-template #loader>
<div class="loader d-flex justify-content-center align-items-center">
<mat-spinner></mat-spinner>
</div>
</ng-template>
3 changes: 3 additions & 0 deletions web-ui/src/app/app.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.loader {
height: 100vh;
}
19 changes: 13 additions & 6 deletions web-ui/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { AuthService, DataService } from './shared/services';
import { AuthService } from './shared/services';
import { UserDataService } from './shared/services';
import { finalize } from 'rxjs';
import { CurrentUserModel } from './shared/interfaces/user-data.interface';

@Component({
selector: 'app-root',
Expand All @@ -8,20 +11,24 @@ import { AuthService, DataService } from './shared/services';
})
export class AppComponent implements OnInit {
title = 'mlflow-oidc-auth-front';

name: string = '';
loading = false;
user!: CurrentUserModel;

constructor(
private readonly dataService: DataService,
private readonly userDataService: UserDataService,
private readonly authService: AuthService,
) {
}

ngOnInit(): void {
this.dataService.getCurrentUser()
this.loading = false;
this.userDataService.getCurrentUser()
.pipe(
finalize(() => this.loading = false),
)
.subscribe((userInfo) => {
this.authService.setUserInfo(userInfo);
this.name = userInfo.display_name;
this.user = userInfo;
});
}
}
5 changes: 5 additions & 0 deletions web-ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SharedModule } from './shared/shared.module';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorHandlerInterceptor } from './shared/interceptors/error-handler.interceptor';

@NgModule({
declarations: [
Expand All @@ -16,6 +18,9 @@ import { SharedModule } from './shared/shared.module';
BrowserAnimationsModule,
SharedModule,
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ErrorHandlerInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
18 changes: 18 additions & 0 deletions web-ui/src/app/core/configs/api-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const API_URL = {
ALL_EXPERIMENTS: '/api/2.0/mlflow/experiments',
EXPERIMENTS_FOR_USER: '/api/2.0/mlflow/users/${userName}/experiments',
USERS_FOR_EXPERIMENT: '/api/2.0/mlflow/experiments/${experimentName}/users',
ALL_MODELS: '/api/2.0/mlflow/registered-models',
MODELS_FOR_USER: '/api/2.0/mlflow/users/${userName}/registered-models',
USERS_FOR_MODEL: '/api/2.0/mlflow/registered-models/${modelName}/users',
CREATE_EXPERIMENT_PERMISSION: '/api/2.0/mlflow/experiments/permissions/create',
CREATE_MODEL_PERMISSION: '/api/2.0/mlflow/registered-models/permissions/create',
UPDATE_EXPERIMENT_PERMISSION: '/api/2.0/mlflow/experiments/permissions/update',
UPDATE_MODEL_PERMISSION: '/api/2.0/mlflow/registered-models/permissions/update',
DELETE_EXPERIMENT_PERMISSION: '/api/2.0/mlflow/experiments/permissions/delete',
DELETE_MODEL_PERMISSION: '/api/2.0/mlflow/registered-models/permissions/delete',

GET_ALL_USERS: '/api/2.0/mlflow/users',
GET_ACCESS_TOKEN: '/api/2.0/mlflow/users/access-token',
GET_CURRENT_USER: '/api/2.0/mlflow/users/current',
};
9 changes: 9 additions & 0 deletions web-ui/src/app/core/configs/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const CORE_CONFIGS = {
SNACK_BAR_DURATION: 7_000,
DEBOUNCE_TIME: 300,
}

export enum EntityEnum {
EXPERIMENT = 'experiment',
MODEL = 'model',
}
20 changes: 20 additions & 0 deletions web-ui/src/app/core/configs/permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export enum PermissionEnum {
EDIT = 'EDIT',
READ = 'READ',
MANAGE = 'MANAGE',
}

export const PERMISSIONS = [
{
value: PermissionEnum.EDIT,
title: 'Edit'
},
{
value: PermissionEnum.READ,
title: 'Read'
},
{
value: PermissionEnum.MANAGE,
title: 'Manage'
}
]
18 changes: 8 additions & 10 deletions web-ui/src/app/features/admin-page/admin-page-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminPageComponent } from './components/admin-page/admin-page.component';
import { PermissionsComponent } from './components/permissions/permissions.component';
import {
UserPermissionDetailsComponent
} from './components/details/user-permission-details/user-permission-details.component';
import {
ExperimentPermissionsComponent
} from './components/permissions/experiment-permissions/experiment-permissions.component';
import { ModelPermissionsComponent } from './components/permissions/model-permissions/model-permissions.component';
AdminPageComponent,
ExperimentPermissionDetailsComponent,
ModelPermissionDetailsComponent,
PermissionsComponent,
UserPermissionDetailsComponent,
} from './components';


const routes: Routes = [
Expand All @@ -26,11 +24,11 @@ const routes: Routes = [
},
{
path: 'experiment/:id',
component: ExperimentPermissionsComponent,
component: ExperimentPermissionDetailsComponent,
},
{
path: 'model/:id',
component: ModelPermissionsComponent,
component: ModelPermissionDetailsComponent,
},
{
path: '**',
Expand Down
28 changes: 13 additions & 15 deletions web-ui/src/app/features/admin-page/admin-page.module.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { AdminPageRoutingModule } from './admin-page-routing.module';
import { AdminPageComponent } from './components/admin-page/admin-page.component';
import { SharedModule } from '../../shared/shared.module';
import { UserPermissionsComponent } from './components/permissions/user-permissions/user-permissions.component';
import {
ExperimentPermissionsComponent,
} from './components/permissions/experiment-permissions/experiment-permissions.component';
import { ModelPermissionsComponent } from './components/permissions/model-permissions/model-permissions.component';
import { FormsModule } from '@angular/forms';
import { UserPermissionDetailsComponent } from './components/details/user-permission-details/user-permission-details.component';
import { PermissionsComponent } from './components/permissions/permissions.component';
import { RouterModule } from '@angular/router';

import {
AdminPageComponent,
ExperimentPermissionDetailsComponent,
} from './components/details/experiment-permission-details/experiment-permission-details.component';
import { ModelPermisionDetailsComponent } from './components/details/model-permision-details/model-permision-details.component';
import { RouterModule } from '@angular/router';
ExperimentPermissionsComponent,
ModelPermissionDetailsComponent,
ModelPermissionsComponent,
PermissionsComponent,
UserPermissionDetailsComponent,
UserPermissionsComponent,
} from './components';
import { AdminPageRoutingModule } from './admin-page-routing.module';
import { SharedModule } from '../../shared/shared.module';


@NgModule({
Expand All @@ -28,7 +26,7 @@ import { RouterModule } from '@angular/router';
UserPermissionDetailsComponent,
PermissionsComponent,
ExperimentPermissionDetailsComponent,
ModelPermisionDetailsComponent,
ModelPermissionDetailsComponent,
],
imports: [
CommonModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
<router-outlet></router-outlet>
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { ActivatedRoute, Router } from '@angular/router';
import { Component } from '@angular/core';

@Component({
selector: 'ml-admin-page',
templateUrl: './admin-page.component.html',
styleUrls: ['./admin-page.component.scss'],
template: '<router-outlet></router-outlet>',
})
export class AdminPageComponent implements OnInit {

constructor(
private readonly router: Router,
private readonly route: ActivatedRoute,
) {
}

ngOnInit(): void {
}

foo(event: MatTabChangeEvent) {
const mapping: { [key: number]: string } = {
0: '/admin/user-permissions',
1: '/admin/experiments-permissions',
2: '/admin/models-permissions',
}
const route = mapping[event.index];
if (route) {
void this.router.navigate([route], { relativeTo: this.route });
}
}
}
export class AdminPageComponent {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="my-3">
<div class="header-section">
<h3>Experiment Access</h3>
<button mat-button>
<button mat-button (click)="addUser()">
<mat-icon>add</mat-icon>
<span>Add</span>
</button>
Expand All @@ -10,7 +10,7 @@ <h3>Experiment Access</h3>
<ml-table
[columnConfig]="userColumnConfig"
[data]="userDataSource"
[isActionsActive]="true"
(editEvent)="handleUserEdit($event)"
[actions]="actions"
(action)="handleActions($event)"
></ml-table>
</div>
Loading

0 comments on commit 9ce176e

Please sign in to comment.