Skip to content

Commit

Permalink
feat: Provide diagram cache to sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
MoritzWeber0 committed Feb 20, 2025
1 parent 9176abe commit f87b175
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 20 deletions.
61 changes: 61 additions & 0 deletions backend/capellacollab/projects/toolmodels/diagrams/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import datetime
import logging

import requests
from sqlalchemy import orm

from capellacollab.configuration.app import config
from capellacollab.projects.toolmodels.modelsources.git import (
models as git_models,
)
from capellacollab.projects.toolmodels.modelsources.git.handler import (
factory as git_handler_factory,
)
from capellacollab.projects.toolmodels.modelsources.git.handler import (
handler as git_handler,
)

from . import exceptions


async def fetch_diagram_cache_metadata(
logger: logging.LoggerAdapter, handler: git_handler.GitHandler
) -> tuple[str | None, datetime.datetime, bytes]:
try:
return await handler.get_file_or_artifact(
trusted_file_path="diagram_cache/index.json",
logger=logger,
job_name="update_capella_diagram_cache",
file_revision=f"diagram-cache/{handler.revision}",
)
except requests.HTTPError as e:
logger.info(
"Failed fetching diagram metadata file or artifact for %s",
handler.path,
exc_info=True,
)
raise exceptions.DiagramCacheNotConfiguredProperlyError() from e


async def build_diagram_cache_api_url(
logger: logging.LoggerAdapter,
git_repository: git_models.DatabaseGitModel,
db: orm.Session,
) -> str:
handler = await git_handler_factory.GitHandlerFactory.create_git_handler(
db, git_repository
)
(job_id, _, _) = await fetch_diagram_cache_metadata(logger, handler)

if (config.general.scheme == "http" and config.general.port == 80) or (
config.general.scheme == "https" and config.general.port == 443
):
base_path = f"{config.general.scheme}://{config.general.host}"
else:
base_path = f"{config.general.scheme}://{config.general.host}:{config.general.port}"

job_id_query = f"?job_id={job_id}" if job_id else ""
return f"{base_path}/api/v1/projects/{git_repository.model.project.slug}/models/{git_repository.model.slug}/diagrams/%s{job_id_query}"
25 changes: 6 additions & 19 deletions backend/capellacollab/projects/toolmodels/diagrams/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
handler as git_handler,
)

from . import exceptions
from . import core, exceptions

router = fastapi.APIRouter()

Expand All @@ -54,24 +54,11 @@ async def get_diagram_metadata(
logging.LoggerAdapter, fastapi.Depends(log.get_request_logger)
],
):
try:
(
job_id,
last_updated,
diagram_metadata_entries,
) = await handler.get_file_or_artifact(
trusted_file_path="diagram_cache/index.json",
logger=logger,
job_name="update_capella_diagram_cache",
file_revision=f"diagram-cache/{handler.revision}",
)
except requests.HTTPError as e:
logger.info(
"Failed fetching diagram metadata file or artifact for %s",
handler.path,
exc_info=True,
)
raise exceptions.DiagramCacheNotConfiguredProperlyError() from e
(
job_id,
last_updated,
diagram_metadata_entries,
) = await core.fetch_diagram_cache_metadata(logger, handler)

diagram_metadata_entries = json.loads(diagram_metadata_entries)
return models.DiagramCacheMetadata(
Expand Down
23 changes: 22 additions & 1 deletion backend/capellacollab/sessions/hooks/provisioning.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

import logging
import pathlib
import typing as t

Expand All @@ -20,6 +21,7 @@
injectables as toolmodels_injectables,
)
from capellacollab.projects.toolmodels import models as toolmodels_models
from capellacollab.projects.toolmodels.diagrams import core as diagrams_core
from capellacollab.projects.toolmodels.modelsources.git import (
injectables as git_injectables,
)
Expand Down Expand Up @@ -167,6 +169,7 @@ async def _persistent_provisioning(
entry.revision or git_model.revision,
entry.deep_clone,
request.session_type,
request.logger,
include_credentials=True,
)
)
Expand All @@ -177,6 +180,7 @@ async def _persistent_provisioning(
entry.revision or git_model.revision,
entry.deep_clone,
request.session_type,
request.logger,
include_credentials=False,
)
)
Expand Down Expand Up @@ -208,7 +212,9 @@ def _read_only_provisioning(
cls._get_git_repos_json(
resolved_entries,
request.session_type,
request.logger,
include_credentials=False,
diagram_cache=request.tool.config.provisioning.provide_diagram_cache,
)
)

Expand Down Expand Up @@ -314,7 +320,9 @@ def _get_git_repos_json(
cls,
resolved_entries: list[ResolvedSessionProvisioning],
session_type: sessions_models.SessionType,
logger: logging.LoggerAdapter,
include_credentials: bool = False,
diagram_cache: bool = False,
) -> list[dict[str, str | int]]:
"""Get the git repos as a JSON-serializable list"""
return [
Expand All @@ -324,18 +332,22 @@ def _get_git_repos_json(
entry["entry"].deep_clone,
session_type,
include_credentials,
logger,
diagram_cache,
)
for entry in resolved_entries
]

@classmethod
def _git_model_as_json(
async def _git_model_as_json(
cls,
git_model: git_models.DatabaseGitModel,
revision: str,
deep_clone: bool,
session_type: sessions_models.SessionType,
include_credentials: bool,
logger: logging.LoggerAdapter,
diagram_cache: bool = False,
) -> dict[str, str | int]:
"""Convert a DatabaseGitModel to a JSON-serializable dictionary."""
toolmodel = git_model.model
Expand All @@ -358,9 +370,18 @@ def _git_model_as_json(
/ toolmodel.slug
),
}

if include_credentials and git_model.username:
git_dict["username"] = git_model.username
git_dict["password"] = git_model.password

if diagram_cache:
git_dict[
"diagram_cache"
] = await diagrams_core.build_diagram_cache_api_url(
logger, git_model
)

return git_dict

@classmethod
Expand Down
16 changes: 16 additions & 0 deletions backend/capellacollab/sessions/hooks/session_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
import datetime

from capellacollab.permissions import models as permissions_models
from capellacollab.projects.permissions import (
crud as projects_permissions_crud,
)
from capellacollab.projects.permissions import (
models as projects_permissions_models,
)
from capellacollab.users.tokens import crud as tokens_crud

from . import interface
Expand Down Expand Up @@ -39,6 +45,16 @@ def configuration_hook(
source="session automation",
)

if request.project_scope:
projects_permissions_crud.create_personal_access_token_link(
request.db,
request.project_scope,
token,
projects_permissions_models.ProjectUserScopes(
diagram_cache={permissions_models.UserTokenVerb.GET}
),
)

return interface.ConfigurationHookResult(
environment={"CAPELLACOLLAB_SESSION_API_TOKEN": password},
config={"session_token_id": token.id},
Expand Down
6 changes: 6 additions & 0 deletions backend/capellacollab/tools/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ class ToolModelProvisioning(core_pydantic.BaseModel):
" If enabled and a session without provisioning is requested, it will be declined."
),
)
provide_diagram_cache: bool = pydantic.Field(
default=False,
description=(
"If enabled, the diagram_cache attribute will be added to the provisioning dictionary."
),
)


class PersistentWorkspaceSessionConfiguration(core_pydantic.BaseModel):
Expand Down

0 comments on commit f87b175

Please sign in to comment.