Skip to content

Commit 0f70940

Browse files
committed
create helper to normalize role input (accepts string and Enum)
1 parent 843133c commit 0f70940

File tree

4 files changed

+68
-11
lines changed

4 files changed

+68
-11
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ htmlcov
1212
.pytest_cache
1313
deps
1414
venv
15-
.vscode/settings.json
15+
.vscode/

mergin/client.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@
3939
)
4040
from .client_pull import pull_project_async, pull_project_wait, pull_project_finalize
4141
from .client_push import push_project_async, push_project_wait, push_project_finalize
42-
from .utils import DateTimeEncoder, get_versions_with_file_changes, int_version, is_version_acceptable
42+
from .utils import (
43+
DateTimeEncoder,
44+
get_versions_with_file_changes,
45+
int_version,
46+
is_version_acceptable,
47+
normalize_role,
48+
)
4349
from .version import __version__
4450

4551
this_dir = os.path.dirname(os.path.realpath(__file__))
@@ -1313,7 +1319,7 @@ def create_user(
13131319
email: str,
13141320
password: str,
13151321
workspace_id: int,
1316-
workspace_role: WorkspaceRole,
1322+
workspace_role: str | WorkspaceRole,
13171323
username: str = None,
13181324
notify_user: bool = False,
13191325
) -> dict:
@@ -1328,11 +1334,15 @@ def create_user(
13281334
param notify_user: flag for email notifications - confirmation email will be sent
13291335
"""
13301336
self.check_collaborators_members_support()
1337+
role_enum = normalize_role(workspace_role, WorkspaceRole)
1338+
if role_enum is None:
1339+
raise ValueError("bad role")
1340+
13311341
params = {
13321342
"email": email,
13331343
"password": password,
13341344
"workspace_id": workspace_id,
1335-
"role": workspace_role.value,
1345+
"role": role_enum.value,
13361346
"notify_user": notify_user,
13371347
}
13381348
if username:
@@ -1357,17 +1367,22 @@ def list_workspace_members(self, workspace_id: int) -> typing.List[dict]:
13571367
return json.load(resp)
13581368

13591369
def update_workspace_member(
1360-
self, workspace_id: int, user_id: int, workspace_role: WorkspaceRole, reset_projects_roles: bool = False
1370+
self, workspace_id: int, user_id: int, workspace_role: str | WorkspaceRole, reset_projects_roles: bool = False
13611371
) -> dict:
13621372
"""
13631373
Update workspace role of a workspace member, optionally resets the projects role
13641374
13651375
param reset_projects_roles: all project specific roles will be removed
13661376
"""
13671377
self.check_collaborators_members_support()
1378+
1379+
role_enum = normalize_role(workspace_role, WorkspaceRole)
1380+
if role_enum is None:
1381+
raise ValueError("bad role")
1382+
13681383
params = {
13691384
"reset_projects_roles": reset_projects_roles,
1370-
"workspace_role": workspace_role.value,
1385+
"workspace_role": role_enum.value,
13711386
}
13721387
workspace_member = self.patch(f"v2/workspaces/{workspace_id}/members/{user_id}", params, json_headers)
13731388
return json.load(workspace_member)
@@ -1387,25 +1402,35 @@ def list_project_collaborators(self, project_id: str) -> typing.List[dict]:
13871402
project_collaborators = self.get(f"v2/projects/{project_id}/collaborators")
13881403
return json.load(project_collaborators)
13891404

1390-
def add_project_collaborator(self, project_id: str, user: str, project_role: ProjectRole) -> dict:
1405+
def add_project_collaborator(self, project_id: str, user: str, project_role: ProjectRole | str) -> dict:
13911406
"""
13921407
Add a user to project collaborators and grant them a project role.
13931408
Fails if user is already a member of the project.
13941409
13951410
param user: login (username or email) of the user
13961411
"""
13971412
self.check_collaborators_members_support()
1413+
1414+
role_enum = normalize_role(project_role, WorkspaceRole)
1415+
if role_enum is None:
1416+
raise ValueError("bad role")
1417+
13981418
params = {"role": project_role.value, "user": user}
13991419
project_collaborator = self.post(f"v2/projects/{project_id}/collaborators", params, json_headers)
14001420
return json.load(project_collaborator)
14011421

1402-
def update_project_collaborator(self, project_id: str, user_id: int, project_role: ProjectRole) -> dict:
1422+
def update_project_collaborator(self, project_id: str, user_id: int, project_role: ProjectRole | str) -> dict:
14031423
"""
14041424
Update project role of the existing project collaborator.
14051425
Fails if user is not a member of the project yet.
14061426
"""
14071427
self.check_collaborators_members_support()
1428+
1429+
role_enum = normalize_role(project_role, WorkspaceRole)
1430+
if role_enum is None:
1431+
raise ValueError("bad role")
14081432
params = {"role": project_role.value}
1433+
14091434
project_collaborator = self.patch(f"v2/projects/{project_id}/collaborators/{user_id}", params, json_headers)
14101435
return json.load(project_collaborator)
14111436

@@ -1481,13 +1506,18 @@ def send_logs(
14811506
request = urllib.request.Request(url, data=payload, headers=header)
14821507
return self._do_request(request)
14831508

1484-
def create_invitation(self, workspace_id: int, email: str, workspace_role: WorkspaceRole):
1509+
def create_invitation(self, workspace_id: int, email: str, workspace_role: str | WorkspaceRole):
14851510
"""
14861511
Create invitation to workspace for specific role
14871512
"""
14881513
min_version = "2025.6.1"
14891514
if not is_version_acceptable(self.server_version(), min_version):
14901515
raise NotImplementedError(f"This needs server at version {min_version} or later")
1491-
params = {"email": email, "role": workspace_role.value}
1516+
1517+
role_enum = normalize_role(workspace_role, WorkspaceRole)
1518+
if role_enum is None:
1519+
raise ValueError("bad role")
1520+
1521+
params = {"email": email, "role": role_enum.value}
14921522
ws_inv = self.post(f"v2/workspaces/{workspace_id}/invitations", params, json_headers)
14931523
return json.load(ws_inv)

mergin/test/test_client.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
unique_path_name,
3939
conflicted_copy_file_name,
4040
edit_conflict_file_name,
41+
normalize_role,
4142
)
4243
from ..merginproject import pygeodiff
4344
from ..report import create_report
@@ -3026,3 +3027,16 @@ def test_server_type(mc):
30263027
mock_client_get.side_effect = ClientError(detail="Service unavailable", http_error=503)
30273028
with pytest.raises(ClientError, match="Service unavailable"):
30283029
mc.server_type()
3030+
3031+
3032+
def test_string_roles():
3033+
assert normalize_role("guest", WorkspaceRole) == WorkspaceRole.GUEST
3034+
assert normalize_role(" GuEsT ", WorkspaceRole) == WorkspaceRole.GUEST
3035+
assert normalize_role("writer", ProjectRole) == ProjectRole.WRITER
3036+
assert normalize_role(" WRITER ", ProjectRole) == ProjectRole.WRITER
3037+
3038+
assert normalize_role("guuuest", WorkspaceRole) is None
3039+
assert normalize_role("ownerr", ProjectRole) is None
3040+
assert normalize_role("", WorkspaceRole) is None
3041+
assert normalize_role(None, WorkspaceRole) is None
3042+
assert normalize_role(123, WorkspaceRole) is None

mergin/utils.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from datetime import datetime
88
from pathlib import Path
99
import tempfile
10-
from .common import ClientError
10+
from .common import ClientError, WorkspaceRole
1111

1212

1313
def generate_checksum(file, chunk_size=4096):
@@ -309,3 +309,16 @@ def cleanup_tmp_dir(mp, tmp_dir: tempfile.TemporaryDirectory):
309309
mp.log.warning(f"Permission error during tmp dir cleanup: {tmp_dir.name}")
310310
except Exception as e:
311311
mp.log.error(f"Error during tmp dir cleanup: {tmp_dir.name}: {e}")
312+
313+
314+
def normalize_role(role, enum_cls):
315+
if isinstance(role, enum_cls):
316+
return role
317+
318+
if isinstance(role, str):
319+
try:
320+
return enum_cls(role.strip().lower())
321+
except ValueError:
322+
return None
323+
324+
return None

0 commit comments

Comments
 (0)