Skip to content

Commit 0d6f59a

Browse files
yt-msMidnighter
authored andcommitted
feat(API): lock workspace in with block through lock() method
1 parent 9e2e5a2 commit 0d6f59a

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ History
55
Next Release
66
------------
77
* Feat: Add support for grouping elements (#72)
8+
* Feat: Locking workspace in ``with`` block through ``lock()`` method (#62)
89

910
0.4.0 (2021-02-05)
1011
------------------

src/structurizr/api/structurizr_client.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import hmac
2222
import logging
2323
from base64 import b64encode
24+
from contextlib import contextmanager
2425
from datetime import datetime, timedelta, timezone
2526
from pathlib import Path
2627
from typing import Dict
@@ -99,7 +100,11 @@ def __repr__(self) -> str:
99100
)
100101

101102
def __enter__(self):
102-
"""Enter a context by locking the corresponding remote workspace."""
103+
"""Enter a context by locking the corresponding remote workspace.
104+
105+
Note: this method is deprecated in favour of lock(), and will be removed in a
106+
future relesae.
107+
"""
103108
is_successful = self.lock_workspace()
104109
if not is_successful:
105110
raise StructurizrClientException(
@@ -109,14 +114,36 @@ def __enter__(self):
109114
return self
110115

111116
def __exit__(self, exc_type, exc_val, exc_tb):
112-
"""Exit a context by unlocking the corresponding remote workspace."""
117+
"""Exit a context by unlocking the corresponding remote workspace.
118+
119+
Note: this method is deprecated in favour of lock(), and will be removed in a
120+
future relesae.
121+
"""
113122
is_successful = self.unlock_workspace()
114123
self._client.close()
115124
if exc_type is None and not is_successful:
116125
raise StructurizrClientException(
117126
f"Failed to unlock the Structurizr workspace {self.workspace_id}."
118127
)
119128

129+
@contextmanager
130+
def lock(self):
131+
"""Provide a context manager for locking and unlocking a workspace."""
132+
is_successful = self.lock_workspace()
133+
if not is_successful:
134+
raise StructurizrClientException(
135+
f"Failed to lock the Structurizr workspace {self.workspace_id}."
136+
)
137+
try:
138+
yield None
139+
finally:
140+
is_successful = self.unlock_workspace()
141+
self._client.close()
142+
if not is_successful:
143+
raise StructurizrClientException(
144+
f"Failed to unlock the Structurizr workspace {self.workspace_id}."
145+
)
146+
120147
def close(self) -> None:
121148
"""Close the connection pool."""
122149
self._client.close()

tests/unit/api/test_structurizr_client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,29 @@ def fake_send(request: Request):
210210
assert requests[0].url.path == "/workspace/19/lock"
211211
assert requests[1].method == "DELETE"
212212
assert requests[1].url.path == "/workspace/19/lock"
213+
214+
215+
def test_locking_and_unlocking_with_context_manager(
216+
client: StructurizrClient, mocker: MockerFixture
217+
):
218+
"""Check new-style locking using .lock()."""
219+
requests: List[Request] = []
220+
221+
def fake_send(request: Request):
222+
nonlocal requests
223+
requests.append(request)
224+
return Response(
225+
200,
226+
content='{"success": true, "message": "OK"}'.encode("ascii"),
227+
request=request,
228+
)
229+
230+
mocker.patch.object(client._client, "send", new=fake_send)
231+
with client.lock():
232+
pass
233+
234+
assert len(requests) == 2
235+
assert requests[0].method == "PUT"
236+
assert requests[0].url.path == "/workspace/19/lock"
237+
assert requests[1].method == "DELETE"
238+
assert requests[1].url.path == "/workspace/19/lock"

0 commit comments

Comments
 (0)