Content-Length: 640230 | pFad | http://github.com/stacklok/codegate/commit/b40cef7e8832deaaedbf48cad4357bba4fab9648

0F Implement basic soft deletion for workspaces · stacklok/codegate@b40cef7 · GitHub
Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b40cef7

Browse files
committedJan 20, 2025
Implement basic soft deletion for workspaces
this adds a `deleted_at` column to workspaces that implements a basic soft-deletion mechanism. All relevant queries have been modified to reflect this. At the moment, there is no hard deletion of workspaces; this will be implemented in the future. We also have no way of showing "archived" or "soft-deleted" workspaces. This will come in due time. Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>
1 parent 28af062 commit b40cef7

File tree

5 files changed

+102
-8
lines changed

5 files changed

+102
-8
lines changed
 

Diff for: ‎migrations/versions/8e4b4b8d1a88_add_soft_delete.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""add soft delete
2+
3+
Revision ID: 8e4b4b8d1a88
4+
Revises: 5c2f3eee5f90
5+
Create Date: 2025-01-20 14:08:40.851647
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
from alembic import op
12+
13+
# revision identifiers, used by Alembic.
14+
revision: str = "8e4b4b8d1a88"
15+
down_revision: Union[str, None] = "5c2f3eee5f90"
16+
branch_labels: Union[str, Sequence[str], None] = None
17+
depends_on: Union[str, Sequence[str], None] = None
18+
19+
20+
def upgrade() -> None:
21+
op.execute(
22+
"""
23+
ALTER TABLE workspaces
24+
ADD COLUMN deleted_at DATETIME DEFAULT NULL;
25+
"""
26+
)
27+
28+
29+
def downgrade() -> None:
30+
op.execute("ALTER TABLE workspaces DROP COLUMN deleted_at;")

Diff for: ‎src/codegate/api/v1.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,14 @@ async def create_workspace(request: v1_models.CreateWorkspaceRequest) -> v1_mode
7979
"/workspaces/{workspace_name}",
8080
tags=["Workspaces"],
8181
generate_unique_id_function=uniq_name,
82-
status_code=204,
8382
)
8483
async def delete_workspace(workspace_name: str):
8584
"""Delete a workspace by name."""
86-
raise NotImplementedError
85+
try:
86+
_ = await wscrud.soft_delete_workspace(workspace_name)
87+
except crud.WorkspaceDoesNotExistError:
88+
return HTTPException(status_code=404, detail="Workspace does not exist")
89+
except Exception:
90+
return HTTPException(status_code=500, detail="Internal server error")
91+
92+
return Response(status_code=204)

Diff for: ‎src/codegate/db/connection.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,20 @@ async def update_session(self, session: Session) -> Optional[Session]:
290290
active_session = await self._execute_update_pydantic_model(session, sql, should_raise=True)
291291
return active_session
292292

293+
async def soft_delete_workspace(self, workspace: Workspace) -> Optional[Workspace]:
294+
sql = text(
295+
"""
296+
UPDATE workspaces
297+
SET deleted_at = CURRENT_TIMESTAMP
298+
WHERE id = :id
299+
RETURNING *
300+
"""
301+
)
302+
deleted_workspace = await self._execute_update_pydantic_model(
303+
workspace, sql, should_raise=True
304+
)
305+
return deleted_workspace
306+
293307

294308
class DbReader(DbCodeGate):
295309

@@ -387,18 +401,19 @@ async def get_workspaces(self) -> List[WorkspaceActive]:
387401
w.id, w.name, s.active_workspace_id
388402
FROM workspaces w
389403
LEFT JOIN sessions s ON w.id = s.active_workspace_id
404+
WHERE w.deleted_at IS NULL
390405
"""
391406
)
392407
workspaces = await self._execute_select_pydantic_model(WorkspaceActive, sql)
393408
return workspaces
394409

395-
async def get_workspace_by_name(self, name: str) -> List[Workspace]:
410+
async def get_workspace_by_name(self, name: str) -> Workspace:
396411
sql = text(
397412
"""
398413
SELECT
399414
id, name
400415
FROM workspaces
401-
WHERE name = :name
416+
WHERE name = :name AND deleted_at IS NULL
402417
"""
403418
)
404419
conditions = {"name": name}

Diff for: ‎src/codegate/pipeline/cli/commands.py

+20
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __init__(self):
4646
"list": self._list_workspaces,
4747
"add": self._add_workspace,
4848
"activate": self._activate_workspace,
49+
"remove": self._remove_workspace,
4950
}
5051

5152
async def _list_workspaces(self, *args: List[str]) -> str:
@@ -104,6 +105,25 @@ async def _activate_workspace(self, args: List[str]) -> str:
104105
return "An error occurred while activating the workspace"
105106
return f"Workspace **{workspace_name}** has been activated"
106107

108+
async def _remove_workspace(self, args: List[str]) -> str:
109+
"""
110+
Remove a workspace
111+
"""
112+
if args is None or len(args) == 0:
113+
return "Please provide a name. Use `codegate workspace remove workspace_name`"
114+
115+
workspace_name = args[0]
116+
if not workspace_name:
117+
return "Please provide a name. Use `codegate workspace remove workspace_name`"
118+
119+
try:
120+
await self.workspace_crud.soft_delete_workspace(workspace_name)
121+
except crud.WorkspaceDoesNotExistError:
122+
return f"Workspace **{workspace_name}** does not exist"
123+
except Exception:
124+
return "An error occurred while removing the workspace"
125+
return f"Workspace **{workspace_name}** has been removed"
126+
107127
async def run(self, args: List[str]) -> str:
108128
if not args:
109129
return "Please provide a command. Use `codegate workspace -h` to see available commands"

Diff for: ‎src/codegate/workspaces/crud.py

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import datetime
22
from typing import List, Optional, Tuple
33

4+
import structlog
5+
46
from codegate.db.connection import DbReader, DbRecorder
57
from codegate.db.models import ActiveWorkspace, Session, Workspace, WorkspaceActive
68

@@ -68,10 +70,6 @@ async def _is_workspace_active(
6870
async def activate_workspace(self, workspace_name: str):
6971
"""
7072
Activate a workspace
71-
72-
Will return:
73-
- True if the workspace was activated
74-
- False if the workspace is already active or does not exist
7573
"""
7674
is_active, session, workspace = await self._is_workspace_active(workspace_name)
7775
if is_active:
@@ -82,3 +80,28 @@ async def activate_workspace(self, workspace_name: str):
8280
db_recorder = DbRecorder()
8381
await db_recorder.update_session(session)
8482
return
83+
84+
async def soft_delete_workspace(self, workspace_name: str):
85+
"""
86+
Soft delete a workspace
87+
"""
88+
if workspace_name == "":
89+
raise WorkspaceCrudError("Workspace name cannot be empty.")
90+
if workspace_name == "default":
91+
raise WorkspaceCrudError("Cannot delete default workspace.")
92+
93+
selected_workspace = await self._db_reader.get_workspace_by_name(workspace_name)
94+
if not selected_workspace:
95+
raise WorkspaceDoesNotExistError(f"Workspace {workspace_name} does not exist.")
96+
97+
# Check if workspace is active, if it is, make the default workspace active
98+
active_workspace = await self._db_reader.get_active_workspace()
99+
if active_workspace and active_workspace.id == selected_workspace.id:
100+
_ = await self.activate_workspace("default")
101+
102+
db_recorder = DbRecorder()
103+
try:
104+
_ = await db_recorder.soft_delete_workspace(selected_workspace)
105+
except Exception as e:
106+
raise WorkspaceCrudError(f"Error deleting workspace {workspace_name}")
107+
return

0 commit comments

Comments
 (0)
Failed to load comments.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/stacklok/codegate/commit/b40cef7e8832deaaedbf48cad4357bba4fab9648

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy