From 13951a6542d5b50083ab1f3e910d5cd177875fb5 Mon Sep 17 00:00:00 2001 From: Paula-Kli Date: Thu, 28 Sep 2023 15:47:17 +0200 Subject: [PATCH] feat: Move model from one project to another --- backend/capellacollab/projects/routes.py | 45 ++++++++++++++ .../capellacollab/projects/toolmodels/crud.py | 11 ++++ .../projects/toolmodels/routes.py | 26 +++++++++ frontend/src/app/app.module.ts | 2 + .../projects/models/service/model.service.ts | 17 ++++++ .../model-overview.component.html | 8 +++ .../model-overview.component.ts | 11 ++++ .../move-model/move-model.component.css | 4 ++ .../move-model/move-model.component.html | 42 ++++++++++++++ .../move-model/move-model.component.ts | 58 +++++++++++++++++++ .../app/projects/service/project.service.ts | 9 +++ 11 files changed, 233 insertions(+) create mode 100644 frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.css create mode 100644 frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.html create mode 100644 frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.ts diff --git a/backend/capellacollab/projects/routes.py b/backend/capellacollab/projects/routes.py index ee78f1ee95..39e729b837 100644 --- a/backend/capellacollab/projects/routes.py +++ b/backend/capellacollab/projects/routes.py @@ -66,6 +66,51 @@ def get_projects( return projects +@router.get( + "/role/{role}", + response_model=abc.Sequence[models.Project], + tags=["Projects"], +) +def get_projects_by_role( + role: str, + user: users_models.DatabaseUser = fastapi.Depends( + users_injectables.get_own_user + ), + db: orm.Session = fastapi.Depends(database.get_db), + token=fastapi.Depends(jwt_bearer.JWTBearer()), + log: logging.LoggerAdapter = fastapi.Depends( + core_logging.get_request_logger + ), +) -> abc.Sequence[models.DatabaseProject]: + if auth_injectables.RoleVerification( + required_role=users_models.Role.ADMIN, verify=False + )(token, db): + log.debug("Fetching all projects") + return crud.get_projects(db) + + match role: + case "user": + required_role = projects_users_models.ProjectUserRole.USER + case "manager": + required_role = projects_users_models.ProjectUserRole.MANAGER + case "administrator": + required_role = projects_users_models.ProjectUserRole.ADMIN + case _: + raise fastapi.HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail={"reason": "Requested role does not exist"}, + ) + + projects = [ + association.project + for association in user.projects + if association.role == required_role + ] + + log.debug("Fetching the following projects: %s", projects) + return projects + + @router.patch( "/{project_slug}", response_model=models.Project, diff --git a/backend/capellacollab/projects/toolmodels/crud.py b/backend/capellacollab/projects/toolmodels/crud.py index 8032f945ab..1ea1ba3c77 100644 --- a/backend/capellacollab/projects/toolmodels/crud.py +++ b/backend/capellacollab/projects/toolmodels/crud.py @@ -140,6 +140,17 @@ def update_model( return model +def move_model( + db: orm.Session, + model: models.DatabaseCapellaModel, + project: projects_model.DatabaseProject, +) -> models.DatabaseCapellaModel: + model.project = project + model.project_id = project.id + db.commit() + return model + + def delete_model(db: orm.Session, model: models.DatabaseCapellaModel): db.delete(model) db.commit() diff --git a/backend/capellacollab/projects/toolmodels/routes.py b/backend/capellacollab/projects/toolmodels/routes.py index 069b707ba8..a63d74b15f 100644 --- a/backend/capellacollab/projects/toolmodels/routes.py +++ b/backend/capellacollab/projects/toolmodels/routes.py @@ -147,6 +147,32 @@ def patch_tool_model( return crud.update_model(db, model, body.description, version, nature) +@router.patch( + "/{model_slug}/move", + response_model=models.CapellaModel, + dependencies=[ + fastapi.Depends( + auth_injectables.ProjectRoleVerification( + required_role=projects_users_models.ProjectUserRole.MANAGER + ) + ) + ], + tags=["Projects - Models"], +) +def move_tool_model( + body: dict, + model: models.DatabaseCapellaModel = fastapi.Depends( + injectables.get_existing_capella_model + ), + db: orm.Session = fastapi.Depends(database.get_db), +) -> models.DatabaseCapellaModel: + new_project = projects_injectables.get_existing_project( + body["new_project_slug"], db + ) + + return crud.move_model(db, model, new_project) + + @router.delete( "/{model_slug}", status_code=204, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 89b4c8187d..aa574447eb 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -90,6 +90,7 @@ import { ModelWrapperComponent } from './projects/models/model-wrapper/model-wra import { EditProjectMetadataComponent } from './projects/project-detail/edit-project-metadata/edit-project-metadata.component'; import { ModelComplexityBadgeComponent } from './projects/project-detail/model-overview/model-complexity-badge/model-complexity-badge.component'; import { ModelOverviewComponent } from './projects/project-detail/model-overview/model-overview.component'; +import { MoveModelComponent } from './projects/project-detail/model-overview/move-model/move-model.component'; import { ProjectDetailsComponent } from './projects/project-detail/project-details.component'; import { ProjectMetadataComponent } from './projects/project-detail/project-metadata/project-metadata.component'; import { AddUserToProjectComponent } from './projects/project-detail/project-users/add-user-to-project/add-user-to-project.component'; @@ -184,6 +185,7 @@ import { SettingsComponent } from './settings/settings.component'; ModelOverviewComponent, ModelRestrictionsComponent, ModelWrapperComponent, + MoveModelComponent, NavBarMenuComponent, NoticeComponent, PipelineRunWrapperComponent, diff --git a/frontend/src/app/projects/models/service/model.service.ts b/frontend/src/app/projects/models/service/model.service.ts index 318c8d859e..0ef2333a6e 100644 --- a/frontend/src/app/projects/models/service/model.service.ts +++ b/frontend/src/app/projects/models/service/model.service.ts @@ -145,6 +145,23 @@ export class ModelService { ); }; } + + moveModelToProject( + projectSlug: string, + modelSlug: string, + new_project_slug: string + ): Observable { + return this.http + .patch(`${this.backendURLFactory(projectSlug, modelSlug)}/move`, { + new_project_slug, + }) + .pipe( + tap(() => { + this.loadModels(projectSlug); + this._model.next(undefined); + }) + ); + } } export type NewModel = { diff --git a/frontend/src/app/projects/project-detail/model-overview/model-overview.component.html b/frontend/src/app/projects/project-detail/model-overview/model-overview.component.html index db1e764bad..2394b61c8a 100644 --- a/frontend/src/app/projects/project-detail/model-overview/model-overview.component.html +++ b/frontend/src/app/projects/project-detail/model-overview/model-overview.component.html @@ -93,6 +93,14 @@

Models

> key + { + this.dialog.open(MoveModelComponent, { + height: '40vh', + width: '40vw', + data: { project_slug: project?.slug, model: model }, + }); + }); + } } diff --git a/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.css b/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.css new file mode 100644 index 0000000000..d49deaffd7 --- /dev/null +++ b/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.css @@ -0,0 +1,4 @@ +/* + * SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors + * SPDX-License-Identifier: Apache-2.0 + */ diff --git a/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.html b/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.html new file mode 100644 index 0000000000..eacc3da7ed --- /dev/null +++ b/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.html @@ -0,0 +1,42 @@ + + + +

Move Model to Different Project

+
Select the project to which you want to move the model to.
+ + Search + + search + + + +
{{ project.name }}
+
+
+
+ +
+
diff --git a/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.ts b/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.ts new file mode 100644 index 0000000000..311aaa8438 --- /dev/null +++ b/frontend/src/app/projects/project-detail/model-overview/move-model/move-model.component.ts @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { ToastService } from 'src/app/helpers/toast/toast.service'; +import { + Model, + ModelService, +} from 'src/app/projects/models/service/model.service'; +import { + Project, + ProjectService, +} from 'src/app/projects/service/project.service'; + +@Component({ + selector: 'app-move-model', + templateUrl: './move-model.component.html', + styleUrls: ['./move-model.component.css'], +}) +export class MoveModelComponent { + selectedProject?: Project; + search = ''; + constructor( + private modelService: ModelService, + private router: Router, + private dialogRef: MatDialogRef, + private toastService: ToastService, + public projectService: ProjectService, + @Inject(MAT_DIALOG_DATA) + public data: { project_slug: string; model: Model } + ) { + this.projectService.loadProjectsForRole('manager'); + } + + onProjectSelect(project: Project) { + this.selectedProject = project; + } + + async moveModelToProject(project: Project) { + this.modelService + .moveModelToProject( + this.data.project_slug, + this.data.model.slug, + project.slug + ) + .subscribe(() => { + this.toastService.showSuccess( + 'Model moved', + `The model “${this.data.model.name}” was successfuly moved to project "${this.data.project_slug}".` + ); + this.dialogRef.close(); + }); + } +} diff --git a/frontend/src/app/projects/service/project.service.ts b/frontend/src/app/projects/service/project.service.ts index 0c2a41421f..4776385935 100644 --- a/frontend/src/app/projects/service/project.service.ts +++ b/frontend/src/app/projects/service/project.service.ts @@ -35,6 +35,15 @@ export class ProjectService { }); } + loadProjectsForRole(role: string): void { + this.http + .get(`${this.BACKEND_URL_PREFIX}/role/${role}`) + .subscribe({ + next: (projects) => this._projects.next(projects), + error: () => this._projects.next(undefined), + }); + } + loadProjectBySlug(slug: string): void { this.http.get(`${this.BACKEND_URL_PREFIX}/${slug}`).subscribe({ next: (project) => this._project.next(project),