Skip to content

Commit

Permalink
Merge pull request #1791 from DSD-DBS/check-capella-version-for-linke…
Browse files Browse the repository at this point in the history
…d-repos

feat: Verify compatibility of TeamForCapella model links at all times
  • Loading branch information
MoritzWeber0 authored Sep 26, 2024
2 parents ff3041a + 55a38d9 commit 8a98406
Show file tree
Hide file tree
Showing 39 changed files with 1,096 additions and 526 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import sqlalchemy as sa
from sqlalchemy import orm

from capellacollab.core import database
from capellacollab.projects.toolmodels import models as toolmodels_models
from capellacollab.projects.toolmodels.modelsources.t4c import models
from capellacollab.settings.modelsources.t4c.repositories import (
Expand Down Expand Up @@ -59,10 +58,12 @@ def create_t4c_model(
def patch_t4c_model(
db: orm.Session,
t4c_model: models.DatabaseT4CModel,
patch_model: models.SubmitT4CModel,
repository: repositories_models.DatabaseT4CRepository,
name: str | None = None,
) -> models.DatabaseT4CModel:
database.patch_database_with_pydantic_object(t4c_model, patch_model)

if name is not None:
t4c_model.name = name
t4c_model.repository = repository
db.commit()
return t4c_model

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,39 @@ def __init__(self):
),
err_code="T4C_INTEGRATION_USED_IN_PIPELINES",
)


class T4CIntegrationWrongCapellaVersion(core_exceptions.BaseError):
def __init__(
self,
t4c_server_name: str,
t4c_repository_name: str,
server_version_name: str,
server_version_id: int,
model_version_name: str,
model_version_id: int,
):
super().__init__(
status_code=status.HTTP_400_BAD_REQUEST,
title="The TeamForCapella server is not compatible with Capella model",
reason=(
f"The repository '{t4c_repository_name}' of the TeamForCapella server '{t4c_server_name}' "
f"has version {server_version_name} (ID {server_version_id}), "
f"but the model has version {model_version_name} (ID {model_version_id}). "
"Make sure that those versions match or are compatible with each other."
),
err_code="T4C_INTEGRATION_WRONG_CAPELLA_VERSION",
)


class T4CIntegrationVersionRequired(core_exceptions.BaseError):
def __init__(self, toolmodel_slug: str):
super().__init__(
status_code=status.HTTP_400_BAD_REQUEST,
title="The Capella model requires a version to proceed",
reason=(
f"To link a TeamForCapella repository, the Capella model '{toolmodel_slug}' has to have a version. "
"Please add a version first."
),
err_code="T4C_INTEGRATION_NO_VERSION",
)
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ class SubmitT4CModel(core_pydantic.BaseModel):
t4c_repository_id: int


class PatchT4CModel(core_pydantic.BaseModel):
name: str | None = None
t4c_instance_id: int | None = None
t4c_repository_id: int | None = None


class SimpleT4CModel(core_pydantic.BaseModel):
project_name: str
repository_name: str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
from capellacollab.projects.toolmodels.backups import crud as backups_crud
from capellacollab.projects.users import models as projects_users_models
from capellacollab.settings.modelsources.t4c import (
injectables as settings_t4c_injecatbles,
injectables as settings_t4c_injectables,
)
from capellacollab.settings.modelsources.t4c.repositories import (
injectables as settings_t4c_repositories_injectables,
)
from capellacollab.users import models as users_models

from . import crud, exceptions, injectables, models
from . import crud, exceptions, injectables, models, util

router = fastapi.APIRouter(
dependencies=[
Expand Down Expand Up @@ -87,14 +87,19 @@ def create_t4c_model(
),
db: orm.Session = fastapi.Depends(database.get_db),
):
instance = settings_t4c_injecatbles.get_existing_unarchived_instance(
instance = settings_t4c_injectables.get_existing_unarchived_instance(
body.t4c_instance_id, db
)
repository = (
settings_t4c_repositories_injectables.get_existing_t4c_repository(
body.t4c_repository_id, db, instance
)
)

util.verify_compatibility_of_model_and_server(
model.name, model.version, repository
)

try:
return crud.create_t4c_model(db, model, repository, body.name)
except exc.IntegrityError:
Expand All @@ -118,14 +123,31 @@ def create_t4c_model(
],
),
)
def edit_t4c_model(
body: models.SubmitT4CModel,
def update_t4c_model(
body: models.PatchT4CModel,
t4c_model: models.DatabaseT4CModel = fastapi.Depends(
injectables.get_existing_t4c_model
),
db: orm.Session = fastapi.Depends(database.get_db),
):
return crud.patch_t4c_model(db, t4c_model, body)
if body.t4c_instance_id is not None:
instance = settings_t4c_injectables.get_existing_unarchived_instance(
body.t4c_instance_id, db
)
else:
instance = t4c_model.repository.instance

repository = (
settings_t4c_repositories_injectables.get_existing_t4c_repository(
body.t4c_repository_id or t4c_model.repository.id, db, instance
)
)

util.verify_compatibility_of_model_and_server(
t4c_model.model.name, t4c_model.model.version, repository
)

return crud.patch_t4c_model(db, t4c_model, repository, body.name)


@router.delete(
Expand Down
32 changes: 32 additions & 0 deletions backend/capellacollab/projects/toolmodels/modelsources/t4c/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from capellacollab.settings.modelsources.t4c.repositories import (
models as t4c_repository_models,
)
from capellacollab.tools import models as tools_models

from . import exceptions


def verify_compatibility_of_model_and_server(
model_name: str,
model_version: tools_models.DatabaseVersion | None,
t4c_repository: t4c_repository_models.DatabaseT4CRepository,
):
server = t4c_repository.instance
if model_version is None:
raise exceptions.T4CIntegrationVersionRequired(model_name)

if (
t4c_repository.instance.version.id
not in model_version.config.compatible_versions + [model_version.id]
):
raise exceptions.T4CIntegrationWrongCapellaVersion(
server.name,
t4c_repository.name,
server.version.name,
server.version.id,
model_version.name,
model_version.id,
)
23 changes: 14 additions & 9 deletions backend/capellacollab/projects/toolmodels/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

import fastapi
import slugify
from sqlalchemy import exc, orm
from sqlalchemy import orm

from capellacollab.core import database
from capellacollab.core import exceptions as core_exceptions
from capellacollab.core.authentication import injectables as auth_injectables
from capellacollab.projects import injectables as projects_injectables
from capellacollab.projects import models as projects_models
from capellacollab.projects.toolmodels.modelsources.t4c import util as t4c_util
from capellacollab.projects.users import models as projects_users_models
from capellacollab.tools import injectables as tools_injectables
from capellacollab.users import injectables as users_injectables
Expand Down Expand Up @@ -85,14 +86,13 @@ def create_new_tool_model(
if tool.integrations.jupyter:
configuration["workspace"] = str(uuid.uuid4())

try:
model = crud.create_model(
db, project, new_model, tool, configuration=configuration
)
except exc.IntegrityError:
raise exceptions.ToolModelAlreadyExistsError(
project.slug, slugify.slugify(new_model.name)
)
slug = slugify.slugify(new_model.name)
if crud.get_model_by_slugs(db, project.slug, slug):
raise exceptions.ToolModelAlreadyExistsError(project.slug, slug)

model = crud.create_model(
db, project, new_model, tool, configuration=configuration
)

if tool.integrations.jupyter:
workspace.create_shared_workspace(
Expand Down Expand Up @@ -153,6 +153,11 @@ def patch_tool_model(
else model.nature
)

for t4c_model in model.t4c_models:
t4c_util.verify_compatibility_of_model_and_server(
model.name, version, t4c_model.repository
)

if body.project_slug:
new_project = determine_new_project_to_move_model(
body.project_slug, db, user
Expand Down
1 change: 0 additions & 1 deletion backend/capellacollab/settings/modelsources/t4c/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ class PatchT4CInstance(core_pydantic.BaseModel):
username: str | None = None
password: str | None = None
protocol: Protocol | None = None
version_id: int | None = None
is_archived: bool | None = None

_validate_rest_api_url = pydantic.field_validator("rest_api")(
Expand Down
32 changes: 0 additions & 32 deletions backend/tests/projects/toolmodels/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
import capellacollab.projects.models as projects_models
import capellacollab.projects.toolmodels.crud as toolmodels_crud
import capellacollab.projects.toolmodels.models as toolmodels_models
import capellacollab.projects.toolmodels.modelsources.git.crud as project_git_crud
import capellacollab.projects.toolmodels.modelsources.git.models as project_git_models
import capellacollab.projects.toolmodels.modelsources.t4c.crud as models_t4c_crud
import capellacollab.projects.toolmodels.modelsources.t4c.models as models_t4c_models
import capellacollab.settings.modelsources.t4c.repositories.models as settings_t4c_repositories_models
import capellacollab.tools.models as tools_models


Expand Down Expand Up @@ -45,30 +40,3 @@ def fixture_jupyter_model(
tool=jupyter_tool,
configuration={"workspace": "test"},
)


@pytest.fixture(name="git_model")
def fixture_git_model(
db: orm.Session, capella_model: toolmodels_models.DatabaseToolModel
) -> project_git_models.DatabaseGitModel:
git_model = project_git_models.PostGitModel(
path="https://example.com/test/project",
entrypoint="test/test.aird",
revision="main",
username="user",
password="password",
)
return project_git_crud.add_git_model_to_capellamodel(
db, capella_model, git_model
)


@pytest.fixture(name="t4c_model")
def fixture_t4c_model(
db: orm.Session,
capella_model: toolmodels_models.DatabaseToolModel,
t4c_repository: settings_t4c_repositories_models.DatabaseT4CRepository,
) -> models_t4c_models.DatabaseT4CModel:
return models_t4c_crud.create_t4c_model(
db, capella_model, t4c_repository, "default"
)
39 changes: 39 additions & 0 deletions backend/tests/projects/toolmodels/modelsources/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import pytest
from sqlalchemy import orm

import capellacollab.projects.toolmodels.models as toolmodels_models
import capellacollab.projects.toolmodels.modelsources.git.crud as project_git_crud
import capellacollab.projects.toolmodels.modelsources.git.models as project_git_models
import capellacollab.projects.toolmodels.modelsources.t4c.crud as models_t4c_crud
import capellacollab.projects.toolmodels.modelsources.t4c.models as models_t4c_models
import capellacollab.settings.modelsources.t4c.repositories.models as settings_t4c_repositories_models


@pytest.fixture(name="t4c_model")
def fixture_t4c_model(
db: orm.Session,
capella_model: toolmodels_models.DatabaseToolModel,
t4c_repository: settings_t4c_repositories_models.DatabaseT4CRepository,
) -> models_t4c_models.DatabaseT4CModel:
return models_t4c_crud.create_t4c_model(
db, capella_model, t4c_repository, "default"
)


@pytest.fixture(name="git_model")
def fixture_git_model(
db: orm.Session, capella_model: toolmodels_models.DatabaseToolModel
) -> project_git_models.DatabaseGitModel:
git_model = project_git_models.PostGitModel(
path="https://example.com/test/project",
entrypoint="test/test.aird",
revision="main",
username="user",
password="password",
)
return project_git_crud.add_git_model_to_capellamodel(
db, capella_model, git_model
)
Loading

0 comments on commit 8a98406

Please sign in to comment.