diff --git a/backend/capellacollab/projects/toolmodels/crud.py b/backend/capellacollab/projects/toolmodels/crud.py index 8032f945ab..b8539ae704 100644 --- a/backend/capellacollab/projects/toolmodels/crud.py +++ b/backend/capellacollab/projects/toolmodels/crud.py @@ -129,6 +129,7 @@ def update_model( db: orm.Session, model: models.DatabaseCapellaModel, description: str | None, + name: str | None, version: tools_models.DatabaseVersion, nature: tools_models.DatabaseNature, ) -> models.DatabaseCapellaModel: @@ -136,6 +137,9 @@ def update_model( model.nature = nature if description: model.description = description + if name: + model.name = name + model.slug = slugify.slugify(name) db.commit() return model diff --git a/backend/capellacollab/projects/toolmodels/models.py b/backend/capellacollab/projects/toolmodels/models.py index 0ad5badbe1..c01439d2ee 100644 --- a/backend/capellacollab/projects/toolmodels/models.py +++ b/backend/capellacollab/projects/toolmodels/models.py @@ -49,6 +49,7 @@ class PostCapellaModel(pydantic.BaseModel): class PatchCapellaModel(pydantic.BaseModel): + name: str | None = None description: str | None = None version_id: int nature_id: int diff --git a/backend/capellacollab/projects/toolmodels/routes.py b/backend/capellacollab/projects/toolmodels/routes.py index 069b707ba8..dccc25c128 100644 --- a/backend/capellacollab/projects/toolmodels/routes.py +++ b/backend/capellacollab/projects/toolmodels/routes.py @@ -4,6 +4,7 @@ import uuid import fastapi +import slugify from fastapi import status from sqlalchemy import exc, orm @@ -121,11 +122,27 @@ def create_new_tool_model( ) def patch_tool_model( body: models.PatchCapellaModel, + project: projects_models.DatabaseProject = fastapi.Depends( + projects_injectables.get_existing_project + ), model: models.DatabaseCapellaModel = fastapi.Depends( injectables.get_existing_capella_model ), db: orm.Session = fastapi.Depends(database.get_db), ) -> models.DatabaseCapellaModel: + if body.name: + new_slug = slugify.slugify(body.name) + + if model.slug != new_slug and crud.get_model_by_slugs( + db, project.slug, new_slug + ): + raise fastapi.HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail={ + "reason": "A model with a similar name already exists." + }, + ) + version = get_version_by_id_or_raise(db, body.version_id) if version.tool != model.tool: raise fastapi.HTTPException( @@ -144,7 +161,9 @@ def patch_tool_model( }, ) - return crud.update_model(db, model, body.description, version, nature) + return crud.update_model( + db, model, body.description, body.name, version, nature + ) @router.delete( diff --git a/frontend/src/app/projects/models/model-description/model-description.component.html b/frontend/src/app/projects/models/model-description/model-description.component.html index bfdb3d13c8..8f0840fbe4 100644 --- a/frontend/src/app/projects/models/model-description/model-description.component.html +++ b/frontend/src/app/projects/models/model-description/model-description.component.html @@ -7,6 +7,17 @@ {{ (modelService.model$ | async)!.name }}
+
+ + Name + + + A model with a similar name already exists. + +
+
Description @@ -62,7 +73,14 @@ Delete - + diff --git a/frontend/src/app/projects/models/model-description/model-description.component.ts b/frontend/src/app/projects/models/model-description/model-description.component.ts index e375e7574b..801eee57a4 100644 --- a/frontend/src/app/projects/models/model-description/model-description.component.ts +++ b/frontend/src/app/projects/models/model-description/model-description.component.ts @@ -25,6 +25,7 @@ import { ProjectService } from '../../service/project.service'; }) export class ModelDescriptionComponent implements OnInit { form = new FormGroup({ + name: new FormControl(''), description: new FormControl(''), nature: new FormControl(-1), version: new FormControl(-1), @@ -57,7 +58,12 @@ export class ModelDescriptionComponent implements OnInit { model.git_models.length || model.t4c_models.length ); + this.form.controls.name.setAsyncValidators( + this.modelService.asyncSlugValidator(model) + ); + this.form.patchValue({ + name: model.name, description: model.description, nature: model.nature?.id, version: model.version?.id, @@ -83,7 +89,8 @@ export class ModelDescriptionComponent implements OnInit { onSubmit(): void { if (this.form.value && this.modelSlug && this.projectSlug) { this.modelService - .updateModelDescription(this.projectSlug!, this.modelSlug!, { + .updateModelDescription(this.projectSlug, this.modelSlug, { + name: this.form.value.name || undefined, description: this.form.value.description || '', nature_id: this.form.value.nature || undefined, version_id: this.form.value.version || undefined, diff --git a/frontend/src/app/projects/models/service/model.service.ts b/frontend/src/app/projects/models/service/model.service.ts index 318c8d859e..ea8d7afc2b 100644 --- a/frontend/src/app/projects/models/service/model.service.ts +++ b/frontend/src/app/projects/models/service/model.service.ts @@ -132,13 +132,16 @@ export class ModelService { this._models.next(undefined); } - asyncSlugValidator(): AsyncValidatorFn { + asyncSlugValidator(ignoreModel?: Model): AsyncValidatorFn { + const ignoreSlug = !!ignoreModel ? ignoreModel.slug : -1; return (control: AbstractControl): Observable => { const modelSlug = slugify(control.value, { lower: true }); return this.models$.pipe( take(1), map((models) => { - return models?.find((model) => model.slug === modelSlug) + return models?.find( + (model) => model.slug === modelSlug && model.slug !== ignoreSlug + ) ? { uniqueSlug: { value: modelSlug } } : null; }) @@ -168,6 +171,7 @@ export type Model = { }; export type PatchModel = { + name?: string; description?: string; nature_id?: number; version_id?: number;