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 }}
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;