Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Commit f11cbdf

Browse files
authored
Add constructs to rename workspaces (#683)
This adds the CRUD API endpoints and pseudo-CLI subcommands to rename workspaces. Note that `default` and `active` are reserved keywords and cannot be used. Closes: #671 Signed-off-by: Juan Antonio Osorio <[email protected]>
1 parent 599ec26 commit f11cbdf

File tree

4 files changed

+107
-3
lines changed

4 files changed

+107
-3
lines changed

src/codegate/api/v1.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,18 @@ async def activate_workspace(request: v1_models.ActivateWorkspaceRequest, status
6161

6262

6363
@v1.post("/workspaces", tags=["Workspaces"], generate_unique_id_function=uniq_name, status_code=201)
64-
async def create_workspace(request: v1_models.CreateWorkspaceRequest) -> v1_models.Workspace:
64+
async def create_workspace(
65+
request: v1_models.CreateOrRenameWorkspaceRequest,
66+
) -> v1_models.Workspace:
6567
"""Create a new workspace."""
68+
if request.rename_to is not None:
69+
return await rename_workspace(request)
70+
return await create_new_workspace(request)
71+
72+
73+
async def create_new_workspace(
74+
request: v1_models.CreateOrRenameWorkspaceRequest,
75+
) -> v1_models.Workspace:
6676
# Input validation is done in the model
6777
try:
6878
_ = await wscrud.add_workspace(request.name)
@@ -83,6 +93,30 @@ async def create_workspace(request: v1_models.CreateWorkspaceRequest) -> v1_mode
8393
return v1_models.Workspace(name=request.name, is_active=False)
8494

8595

96+
async def rename_workspace(
97+
request: v1_models.CreateOrRenameWorkspaceRequest,
98+
) -> v1_models.Workspace:
99+
try:
100+
_ = await wscrud.rename_workspace(request.name, request.rename_to)
101+
except crud.WorkspaceDoesNotExistError:
102+
raise HTTPException(status_code=404, detail="Workspace does not exist")
103+
except AlreadyExistsError:
104+
raise HTTPException(status_code=409, detail="Workspace already exists")
105+
except ValidationError:
106+
raise HTTPException(
107+
status_code=400,
108+
detail=(
109+
"Invalid workspace name. " "Please use only alphanumeric characters and dashes"
110+
),
111+
)
112+
except crud.WorkspaceCrudError as e:
113+
raise HTTPException(status_code=400, detail=str(e))
114+
except Exception:
115+
raise HTTPException(status_code=500, detail="Internal server error")
116+
117+
return v1_models.Workspace(name=request.rename_to, is_active=False)
118+
119+
86120
@v1.delete(
87121
"/workspaces/{workspace_name}",
88122
tags=["Workspaces"],

src/codegate/api/v1_models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,14 @@ def from_db_workspaces(
4848
)
4949

5050

51-
class CreateWorkspaceRequest(pydantic.BaseModel):
51+
class CreateOrRenameWorkspaceRequest(pydantic.BaseModel):
5252
name: str
5353

54+
# If set, rename the workspace to this name. Note that
55+
# the 'name' field is still required and the workspace
56+
# workspace must exist.
57+
rename_to: Optional[str] = None
58+
5459

5560
class ActivateWorkspaceRequest(pydantic.BaseModel):
5661
name: str

src/codegate/pipeline/cli/commands.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def subcommands(self) -> Dict[str, Callable[[List[str]], Awaitable[str]]]:
154154
"add": self._add_workspace,
155155
"activate": self._activate_workspace,
156156
"remove": self._remove_workspace,
157+
"rename": self._rename_workspace,
157158
}
158159

159160
async def _list_workspaces(self, flags: Dict[str, str], args: List[str]) -> str:
@@ -193,6 +194,37 @@ async def _add_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
193194

194195
return f"Workspace **{new_workspace_name}** has been added"
195196

197+
async def _rename_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
198+
"""
199+
Rename a workspace
200+
"""
201+
if args is None or len(args) < 2:
202+
return (
203+
"Please provide a name and a new name. "
204+
"Use `codegate workspace rename workspace_name new_workspace_name`"
205+
)
206+
207+
old_workspace_name = args[0]
208+
new_workspace_name = args[1]
209+
if not old_workspace_name or not new_workspace_name:
210+
return (
211+
"Please provide a name and a new name. "
212+
"Use `codegate workspace rename workspace_name new_workspace_name`"
213+
)
214+
215+
try:
216+
await self.workspace_crud.rename_workspace(old_workspace_name, new_workspace_name)
217+
except crud.WorkspaceDoesNotExistError:
218+
return f"Workspace **{old_workspace_name}** does not exist"
219+
except AlreadyExistsError:
220+
return f"Workspace **{new_workspace_name}** already exists"
221+
except crud.WorkspaceCrudError:
222+
return "An error occurred while renaming the workspace"
223+
except Exception:
224+
return "An error occurred while renaming the workspace"
225+
226+
return f"Workspace **{old_workspace_name}** has been renamed to **{new_workspace_name}**"
227+
196228
async def _activate_workspace(self, flags: Dict[str, str], args: List[str]) -> str:
197229
"""
198230
Activate a workspace
@@ -249,7 +281,14 @@ def help(self) -> str:
249281
" - `workspace_name`\n\n"
250282
"- `activate`: Activate a workspace\n\n"
251283
" - *args*:\n\n"
252-
" - `workspace_name`"
284+
" - `workspace_name`\n\n"
285+
"- `remove`: Remove a workspace\n\n"
286+
" - *args*:\n\n"
287+
" - `workspace_name`\n\n"
288+
"- `rename`: Rename a workspace\n\n"
289+
" - *args*:\n\n"
290+
" - `workspace_name`\n"
291+
" - `new_workspace_name`\n\n"
253292
)
254293

255294

src/codegate/workspaces/crud.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,32 @@ async def add_workspace(self, new_workspace_name: str) -> Workspace:
4343
workspace_created = await db_recorder.add_workspace(new_workspace_name)
4444
return workspace_created
4545

46+
async def rename_workspace(self, old_workspace_name: str, new_workspace_name: str) -> Workspace:
47+
"""
48+
Rename a workspace
49+
50+
Args:
51+
old_name (str): The old name of the workspace
52+
new_name (str): The new name of the workspace
53+
"""
54+
if new_workspace_name == "":
55+
raise WorkspaceCrudError("Workspace name cannot be empty.")
56+
if old_workspace_name == "":
57+
raise WorkspaceCrudError("Workspace name cannot be empty.")
58+
if old_workspace_name in DEFAULT_WORKSPACE_NAME:
59+
raise WorkspaceCrudError("Cannot rename default workspace.")
60+
if new_workspace_name in RESERVED_WORKSPACE_KEYWORDS:
61+
raise WorkspaceCrudError(f"Workspace name {new_workspace_name} is reserved.")
62+
if old_workspace_name == new_workspace_name:
63+
raise WorkspaceCrudError("Old and new workspace names are the same.")
64+
ws = await self._db_reader.get_workspace_by_name(old_workspace_name)
65+
if not ws:
66+
raise WorkspaceDoesNotExistError(f"Workspace {old_workspace_name} does not exist.")
67+
db_recorder = DbRecorder()
68+
new_ws = Workspace(id=ws.id, name=new_workspace_name, system_prompt=ws.system_prompt)
69+
workspace_renamed = await db_recorder.update_workspace(new_ws)
70+
return workspace_renamed
71+
4672
async def get_workspaces(self) -> List[WorkspaceActive]:
4773
"""
4874
Get all workspaces

0 commit comments

Comments
 (0)