Skip to content

Commit 236161f

Browse files
S0okJuhalucinor
authored andcommitted
feat: Add update project tool (#69)
* feat(identity): Add get-project, get-projects tool(#58) - Add get-project, get-projects tool - Test code are updated and passing * chore(identity): ruff format(#58) * chore(identity): ruff format(#58) * fix(identity): Remove _version.py(#58) * feat(identity): Add create project tool(#58) - Add create-project tool - Tests are updated ans passing * fix(identity): Fix unnecessary test codes(#?58) * feat(identity): Add delete project tool(#58) * feat(identity): Add update project tool(#58)
1 parent 8a3b3b5 commit 236161f

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

src/openstack_mcp_server/tools/identity_tools.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def register_tools(self, mcp: FastMCP):
3030
mcp.tool()(self.get_project)
3131
mcp.tool()(self.create_project)
3232
mcp.tool()(self.delete_project)
33+
mcp.tool()(self.update_project)
3334

3435
def get_regions(self) -> list[Region]:
3536
"""
@@ -319,3 +320,49 @@ def delete_project(self, id: str) -> None:
319320
conn = get_openstack_conn()
320321
conn.identity.delete_project(project=id, ignore_missing=False)
321322
return None
323+
324+
def update_project(
325+
self,
326+
id: str,
327+
name: str | None = None,
328+
description: str | None = None,
329+
is_enabled: bool | None = None,
330+
domain_id: str | None = None,
331+
parent_id: str | None = None,
332+
) -> Project:
333+
"""
334+
Update a project.
335+
336+
:param id: The ID of the project.
337+
:param name: The name of the project.
338+
:param description: The description of the project.
339+
:param is_enabled: Whether the project is enabled.
340+
:param domain_id: The ID of the domain.
341+
:param parent_id: The ID of the parent project.
342+
343+
:return: The updated Project object.
344+
"""
345+
conn = get_openstack_conn()
346+
347+
args = {}
348+
if name is not None:
349+
args["name"] = name
350+
if description is not None:
351+
args["description"] = description
352+
if is_enabled is not None:
353+
args["is_enabled"] = is_enabled
354+
if domain_id is not None:
355+
args["domain_id"] = domain_id
356+
if parent_id is not None:
357+
args["parent_id"] = parent_id
358+
359+
updated_project = conn.identity.update_project(project=id, **args)
360+
361+
return Project(
362+
id=updated_project.id,
363+
name=updated_project.name,
364+
description=updated_project.description,
365+
is_enabled=updated_project.is_enabled,
366+
domain_id=updated_project.domain_id,
367+
parent_id=updated_project.parent_id,
368+
)

tests/tools/test_identity_tools.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,3 +983,73 @@ def test_delete_project_not_found(self, mock_get_openstack_conn_identity):
983983
project="project1111111111111111111111111",
984984
ignore_missing=False,
985985
)
986+
987+
def test_update_project_success(self, mock_get_openstack_conn_identity):
988+
"""Test updating a identity project successfully."""
989+
mock_conn = mock_get_openstack_conn_identity
990+
991+
# Create mock project object
992+
mock_project = Mock()
993+
mock_project.id = "project1111111111111111111111111"
994+
mock_project.name = "ProjectOne"
995+
mock_project.description = "Project One description"
996+
mock_project.is_enabled = True
997+
mock_project.domain_id = "domain1111111111111111111111111"
998+
mock_project.parent_id = "parentproject1111111111111111111"
999+
1000+
# Configure mock project.update_project()
1001+
mock_conn.identity.update_project.return_value = mock_project
1002+
1003+
# Test update_project()
1004+
identity_tools = self.get_identity_tools()
1005+
result = identity_tools.update_project(
1006+
id="project1111111111111111111111111",
1007+
name="ProjectOne",
1008+
description="Project One description",
1009+
is_enabled=True,
1010+
domain_id="domain1111111111111111111111111",
1011+
parent_id="parentproject1111111111111111111",
1012+
)
1013+
1014+
# Verify results
1015+
assert result == Project(
1016+
id="project1111111111111111111111111",
1017+
name="ProjectOne",
1018+
description="Project One description",
1019+
is_enabled=True,
1020+
domain_id="domain1111111111111111111111111",
1021+
parent_id="parentproject1111111111111111111",
1022+
)
1023+
1024+
# Verify mock calls
1025+
mock_conn.identity.update_project.assert_called_once_with(
1026+
project="project1111111111111111111111111",
1027+
name="ProjectOne",
1028+
description="Project One description",
1029+
is_enabled=True,
1030+
domain_id="domain1111111111111111111111111",
1031+
parent_id="parentproject1111111111111111111",
1032+
)
1033+
1034+
def test_update_project_empty_id(self, mock_get_openstack_conn_identity):
1035+
"""Test updating a identity project with an empty ID."""
1036+
mock_conn = mock_get_openstack_conn_identity
1037+
1038+
# Configure mock to raise BadRequestException
1039+
mock_conn.identity.update_project.side_effect = (
1040+
exceptions.BadRequestException(
1041+
"Field required",
1042+
)
1043+
)
1044+
1045+
# Test update_project()
1046+
identity_tools = self.get_identity_tools()
1047+
1048+
with pytest.raises(
1049+
exceptions.BadRequestException,
1050+
match="Field required",
1051+
):
1052+
identity_tools.update_project(id="")
1053+
1054+
# Verify mock calls
1055+
mock_conn.identity.update_project.assert_called_once_with(project="")

0 commit comments

Comments
 (0)