Content-Length: 494864 | pFad | http://github.com/stacklok/codegate/pull/657/files

9F Implement basic soft deletion for workspaces by JAORMX · Pull Request #657 · stacklok/codegate · GitHub
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic soft deletion for workspaces #657

Merged
merged 5 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions migrations/versions/8e4b4b8d1a88_add_soft_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""add soft delete

Revision ID: 8e4b4b8d1a88
Revises: 5c2f3eee5f90
Create Date: 2025-01-20 14:08:40.851647

"""

from typing import Sequence, Union

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "8e4b4b8d1a88"
down_revision: Union[str, None] = "5c2f3eee5f90"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.execute(
"""
ALTER TABLE workspaces
ADD COLUMN deleted_at DATETIME DEFAULT NULL;
"""
)


def downgrade() -> None:
op.execute("ALTER TABLE workspaces DROP COLUMN deleted_at;")
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""merging system prompt and soft-deletes

Revision ID: e6227073183d
Revises: 8e4b4b8d1a88, a692c8b52308
Create Date: 2025-01-20 16:08:40.645298

"""

from typing import Sequence, Union

# revision identifiers, used by Alembic.
revision: str = "e6227073183d"
down_revision: Union[str, None] = ("8e4b4b8d1a88", "a692c8b52308")
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
pass


def downgrade() -> None:
pass
10 changes: 8 additions & 2 deletions src/codegate/api/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,14 @@ async def create_workspace(request: v1_models.CreateWorkspaceRequest) -> v1_mode
"/workspaces/{workspace_name}",
tags=["Workspaces"],
generate_unique_id_function=uniq_name,
status_code=204,
)
async def delete_workspace(workspace_name: str):
"""Delete a workspace by name."""
raise NotImplementedError
try:
_ = await wscrud.soft_delete_workspace(workspace_name)
except crud.WorkspaceDoesNotExistError:
raise HTTPException(status_code=404, detail="Workspace does not exist")
except Exception:
raise HTTPException(status_code=500, detail="Internal server error")

return Response(status_code=204)
17 changes: 16 additions & 1 deletion src/codegate/db/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,20 @@ async def update_session(self, session: Session) -> Optional[Session]:
active_session = await self._execute_update_pydantic_model(session, sql, should_raise=True)
return active_session

async def soft_delete_workspace(self, workspace: Workspace) -> Optional[Workspace]:
sql = text(
"""
UPDATE workspaces
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = :id
RETURNING *
"""
)
deleted_workspace = await self._execute_update_pydantic_model(
workspace, sql, should_raise=True
)
return deleted_workspace


class DbReader(DbCodeGate):

Expand Down Expand Up @@ -401,6 +415,7 @@ async def get_workspaces(self) -> List[WorkspaceActive]:
w.id, w.name, s.active_workspace_id
FROM workspaces w
LEFT JOIN sessions s ON w.id = s.active_workspace_id
WHERE w.deleted_at IS NULL
"""
)
workspaces = await self._execute_select_pydantic_model(WorkspaceActive, sql)
Expand All @@ -412,7 +427,7 @@ async def get_workspace_by_name(self, name: str) -> Optional[Workspace]:
SELECT
id, name, system_prompt
FROM workspaces
WHERE name = :name
WHERE name = :name AND deleted_at IS NULL
"""
)
conditions = {"name": name}
Expand Down
20 changes: 20 additions & 0 deletions src/codegate/pipeline/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def subcommands(self) -> Dict[str, Callable[[List[str]], Awaitable[str]]]:
"list": self._list_workspaces,
"add": self._add_workspace,
"activate": self._activate_workspace,
"remove": self._remove_workspace,
}

async def _list_workspaces(self, flags: Dict[str, str], args: List[str]) -> str:
Expand Down Expand Up @@ -211,6 +212,25 @@ async def _activate_workspace(self, flags: Dict[str, str], args: List[str]) -> s
return "An error occurred while activating the workspace"
return f"Workspace **{workspace_name}** has been activated"

async def _remove_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
"""
Remove a workspace
"""
if args is None or len(args) == 0:
return "Please provide a name. Use `codegate workspace remove workspace_name`"

workspace_name = args[0]
if not workspace_name:
return "Please provide a name. Use `codegate workspace remove workspace_name`"

try:
await self.workspace_crud.soft_delete_workspace(workspace_name)
except crud.WorkspaceDoesNotExistError:
return f"Workspace **{workspace_name}** does not exist"
except Exception:
return "An error occurred while removing the workspace"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should give the user a clearer error message of what's going on. I got this when trying to delete the active workspace. It's ok to fix on a later PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open a bug about this? I can fix it tomorrow.

return f"Workspace **{workspace_name}** has been removed"

@property
def help(self) -> str:
return (
Expand Down
29 changes: 25 additions & 4 deletions src/codegate/workspaces/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ async def _is_workspace_active(
async def activate_workspace(self, workspace_name: str):
"""
Activate a workspace

Will return:
- True if the workspace was activated
- False if the workspace is already active or does not exist
"""
is_active, session, workspace = await self._is_workspace_active(workspace_name)
if is_active:
Expand Down Expand Up @@ -100,6 +96,31 @@ async def update_workspace_system_prompt(
updated_workspace = await db_recorder.update_workspace(workspace_update)
return updated_workspace

async def soft_delete_workspace(self, workspace_name: str):
"""
Soft delete a workspace
"""
if workspace_name == "":
raise WorkspaceCrudError("Workspace name cannot be empty.")
if workspace_name == "default":
raise WorkspaceCrudError("Cannot delete default workspace.")

selected_workspace = await self._db_reader.get_workspace_by_name(workspace_name)
if not selected_workspace:
raise WorkspaceDoesNotExistError(f"Workspace {workspace_name} does not exist.")

# Check if workspace is active, if it is, make the default workspace active
active_workspace = await self._db_reader.get_active_workspace()
if active_workspace and active_workspace.id == selected_workspace.id:
raise WorkspaceCrudError("Cannot delete active workspace.")

db_recorder = DbRecorder()
try:
_ = await db_recorder.soft_delete_workspace(selected_workspace)
except Exception:
raise WorkspaceCrudError(f"Error deleting workspace {workspace_name}")
return

async def get_workspace_by_name(self, workspace_name: str) -> Workspace:
workspace = await self._db_reader.get_workspace_by_name(workspace_name)
if not workspace:
Expand Down
Loading








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/pull/657/files

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy