Skip to content

Commit

Permalink
feat: Set tool configuration with YAML
Browse files Browse the repository at this point in the history
  • Loading branch information
MoritzWeber0 committed Feb 7, 2024
1 parent 1b146ad commit 0c96365
Showing 39 changed files with 1,067 additions and 921 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

"""Migrate tools to JSON configuration
Revision ID: c973be2e2ac7
Revises: 86ab7d4d1684
Create Date: 2024-01-31 17:40:31.743565
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "c973be2e2ac7"
down_revision = "86ab7d4d1684"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("tool_integrations")
op.add_column(
"tools",
sa.Column(
"integrations",
postgresql.JSONB(astext_type=sa.Text()),
nullable=False,
server_default=sa.text("'{}'::jsonb"),
),
)
op.drop_column("tools", "docker_image_backup_template")
op.drop_column("tools", "docker_image_template")
op.drop_column("tools", "readonly_docker_image_template")
op.add_column(
"versions",
sa.Column(
"config",
postgresql.JSONB(astext_type=sa.Text()),
nullable=False,
server_default=sa.text("'{}'::jsonb"),
),
)
op.drop_column("versions", "is_deprecated")
op.drop_column("versions", "is_recommended")
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions backend/capellacollab/core/database/__init__.py
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ class Base(orm.DeclarativeBase):
type_annotation_map = {
dict[str, str]: postgresql.JSONB,
dict[str, t.Any]: postgresql.JSONB,
dict[str, bool]: postgresql.JSONB,
}


64 changes: 64 additions & 0 deletions backend/capellacollab/core/database/decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import typing as t

import pydantic
from sqlalchemy import types
from sqlalchemy.dialects import postgresql


class PydanticDecorator(types.TypeDecorator):
"""Maps a pydantic object to a JSONB column and vice versa.
Use in Database models like this:
```py
json_column: orm.Mapped[pydantic.BaseModel] = orm.mapped_column(PydanticDecorator(pydantic.BaseModel))
```
Replace:
- `json_column` with the name of the column in the database
- `pydantic.BaseModel` with the pydantic model you want to use
"""

impl = postgresql.JSONB
python_type = pydantic.BaseModel

cache_ok = True

def __init__(self, pydantic_model: t.Type[pydantic.BaseModel]):
super().__init__()
self.pydantic_model = pydantic_model

def process_bind_param(self, value, dialect):
"""Convert a pydantic object to JSONB."""
if value is None:
return None
return value.model_dump()

def process_literal_param(self, value, dialect):
"""Convert a literal pydantic object to JSONB."""
if value is None:
return None
return value.model_dump()

def process_result_value(self, value, dialect):
"""Convert JSONB to a pydantic object."""
if value is None:
return None
return self.pydantic_model.model_validate(value)


class PydanticDatabaseModel(pydantic.BaseModel):
"""Base class for database models with an ID.
Use it to extend pydantic models with the database ID field:
```py
class PydanticModel(PydanticSuperModel, decorator.PydanticDatabaseModel):
pass
```
"""

id: int = pydantic.Field(
description="Unique identifier of the resource.", ge=1
)
49 changes: 36 additions & 13 deletions backend/capellacollab/core/database/migration.py
Original file line number Diff line number Diff line change
@@ -36,8 +36,6 @@
)
from capellacollab.tools import crud as tools_crud
from capellacollab.tools import models as tools_models
from capellacollab.tools.integrations import crud as integrations_crud
from capellacollab.tools.integrations import models as integrations_models
from capellacollab.users import crud as users_crud
from capellacollab.users import models as users_models

@@ -120,18 +118,48 @@ def create_tools(db):
if os.getenv("DEVELOPMENT_MODE", "").lower() in ("1", "true", "t"):
capella = tools_models.DatabaseTool(
name="Capella",
docker_image_template=f"{registry}/capella/remote:$version-latest",
docker_image_backup_template=f"{registry}/t4c/client/base:$version-latest",
readonly_docker_image_template=f"{registry}/capella/readonly:$version-latest",
# docker_image_template=f"{registry}/capella/remote:$version-latest",
# docker_image_backup_template=f"{registry}/t4c/client/base:$version-latest",
# readonly_docker_image_template=f"{registry}/capella/readonly:$version-latest",
)

for capella_version in ("5.0.0", "5.2.0", "6.0.0", "6.1.0"):
capella_database_version = tools_models.DatabaseVersion(
name=capella_version,
config=tools_models.ToolVersionConfiguration(
is_recommended=False,
is_deprecated=False,
),
tool=capella,
)
tools_crud.create_version(
db,
capella.id,
capella_database_version,
)

papyrus = tools_models.DatabaseTool(
name="Papyrus",
docker_image_template=f"{registry}/papyrus/client/remote:$version-prod",
integrations=tools_models.ToolIntegrations(
t4c=False, pure_variants=False, jupyter=False
),
)
tools_crud.create_tool(db, papyrus)

tools_crud.create_version(db, papyrus.id, "6.1")
for papyrus_version in ("6.0", "6.1"):
papyrus_database_version = tools_models.DatabaseVersion(
name=papyrus_version,
config=tools_models.ToolVersionConfiguration(
is_recommended=False,
is_deprecated=False,
),
tool=papyrus,
)

tools_crud.create_version(
db,
papyrus.id,
)
tools_crud.create_version(db, papyrus.id, "6.0")

tools_crud.create_nature(db, papyrus.id, "UML 2.5")
@@ -151,14 +179,9 @@ def create_tools(db):

jupyter = tools_models.DatabaseTool(
name="Jupyter",
docker_image_template=f"{registry}/jupyter-notebook:$version",
integrations=tools_models.ToolIntegrations(jupyter=True),
)
tools_crud.create_tool(db, jupyter)
integrations_crud.update_integrations(
db,
jupyter.integrations,
integrations_models.PatchToolIntegrations(jupyter=True),
)

default_version = tools_crud.create_version(db, capella.id, "6.0.0", True)
tools_crud.create_version(db, capella.id, "5.2.0")
1 change: 0 additions & 1 deletion backend/capellacollab/core/database/models.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@
import capellacollab.settings.integrations.purevariants.models
import capellacollab.settings.modelsources.git.models
import capellacollab.settings.modelsources.t4c.models
import capellacollab.tools.integrations.models
import capellacollab.tools.models
import capellacollab.users.models
import capellacollab.users.tokens.models
3 changes: 3 additions & 0 deletions backend/capellacollab/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -56,3 +56,6 @@ def register_exceptions(app: fastapi.FastAPI):
app.add_exception_handler(
ExistingDependenciesError, existing_dependencies_exception_handler # type: ignore[arg-type]
)
app.add_exception_handler(
ResourceAlreadyExistsError, resource_already_exists_exception_handler # type: ignore[arg-type]
)
Original file line number Diff line number Diff line change
@@ -46,7 +46,10 @@ def update_restrictions(
),
db: orm.Session = fastapi.Depends(database.get_db),
) -> models.DatabaseToolModelRestrictions:
if body.allow_pure_variants and not model.tool.integrations.pure_variants:
if (
body.allow_pure_variants
and not model.tool.integrations["pure_variants"]
):
raise fastapi.HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ class DatabaseT4CRepository(database.Base):

class CreateT4CRepository(pydantic.BaseModel):
name: str = pydantic.Field(
pattern="^[-a-zA-Z0-9_]+$", examples=["testrepo"]
pattern=r"^[-a-zA-Z0-9_]+$", examples=["testrepo"]
)


69 changes: 28 additions & 41 deletions backend/capellacollab/tools/crud.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
from sqlalchemy import exc, orm

from capellacollab.core import database
from capellacollab.tools.integrations import models as integrations_models
from capellacollab.tools import models as tools_models

from . import exceptions, models

@@ -35,43 +35,21 @@ def get_tool_by_name(


def create_tool(
db: orm.Session, tool: models.DatabaseTool
db: orm.Session, tool: models.CreateTool
) -> models.DatabaseTool:
tool.integrations = integrations_models.DatabaseToolIntegrations(
pure_variants=False, t4c=False, jupyter=False
database_tool = tools_models.DatabaseTool(
name=tool.name, integrations=tool.integrations
)
db.add(tool)
db.add(database_tool)
db.commit()
return tool


def create_tool_with_name(
db: orm.Session, tool_name: str
) -> models.DatabaseTool:
return create_tool(
db, tool=models.DatabaseTool(name=tool_name, docker_image_template="")
)
return database_tool


def update_tool_name(
db: orm.Session, tool: models.DatabaseTool, tool_name: str
def update_tool(
db: orm.Session, tool: models.DatabaseTool, updated_tool: models.CreateTool
) -> models.DatabaseTool:
tool.name = tool_name
db.commit()
return tool


def update_tool_dockerimages(
db: orm.Session,
tool: models.DatabaseTool,
patch_tool: models.PatchToolDockerimage,
) -> models.DatabaseTool:
if patch_tool.persistent:
tool.docker_image_template = patch_tool.persistent
if patch_tool.readonly:
tool.readonly_docker_image_template = patch_tool.readonly
if patch_tool.backup:
tool.docker_image_backup_template = patch_tool.backup
tool.name = updated_tool.name
tool.integrations = updated_tool.integrations
db.commit()
return tool

@@ -141,9 +119,10 @@ def get_version_by_tool_id_version_name(
def update_version(
db: orm.Session,
version: models.DatabaseVersion,
patch_version: models.UpdateToolVersion,
updated_version: models.CreateToolVersion,
) -> models.DatabaseVersion:
database.patch_database_with_pydantic_object(version, patch_version)
version.name = updated_version.name
version.config = updated_version.config

db.commit()
return version
@@ -152,14 +131,11 @@ def update_version(
def create_version(
db: orm.Session,
tool_id: int,
name: str,
is_recommended: bool = False,
is_deprecated: bool = False,
tool_version: models.CreateToolVersion,
) -> models.DatabaseVersion:
version = models.DatabaseVersion(
name=name,
is_recommended=is_recommended,
is_deprecated=is_deprecated,
name=tool_version.name,
config=tool_version.config,
tool_id=tool_id,
)
db.add(version)
@@ -222,6 +198,17 @@ def get_natures_by_tool_id(
)


def update_nature(
db: orm.Session,
nature: models.DatabaseNature,
updated_version: models.CreateToolNature,
) -> models.DatabaseNature:
nature.name = updated_version.name

db.commit()
return nature


def create_nature(
db: orm.Session, tool_id: int, name: str
) -> models.DatabaseNature:
@@ -261,7 +248,7 @@ def get_backup_image_for_tool_version(db: orm.Session, version_id: int) -> str:
if not (version := get_version_by_id(db, version_id)):
raise exceptions.ToolVersionNotFoundError(version_id)

backup_image_template = version.tool.docker_image_backup_template
backup_image_template = version.config[""]

if not backup_image_template:
raise exceptions.ToolImageNotFoundError(
2 changes: 0 additions & 2 deletions backend/capellacollab/tools/integrations/__init__.py

This file was deleted.

21 changes: 0 additions & 21 deletions backend/capellacollab/tools/integrations/crud.py

This file was deleted.

Loading

0 comments on commit 0c96365

Please sign in to comment.