Skip to content

Commit d87f06b

Browse files
committed
Wrap request and deserialize with versioned method
1 parent 85cd819 commit d87f06b

4 files changed

Lines changed: 101 additions & 83 deletions

File tree

src/blueapi/client/rest.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -170,43 +170,43 @@ def __init__(
170170
self._pool = requests.Session()
171171

172172
def get_plans(self) -> PlanResponse:
173-
return self._request_and_deserialize("/plans", PlanResponse)
173+
return self._request_v1("/plans", PlanResponse)
174174

175175
def get_plan(self, name: str) -> PlanModel:
176-
return self._request_and_deserialize(f"/plans/{name}", PlanModel)
176+
return self._request_v1(f"/plans/{name}", PlanModel)
177177

178178
def get_devices(self) -> DeviceResponse:
179-
return self._request_and_deserialize("/devices", DeviceResponse)
179+
return self._request_v1("/devices", DeviceResponse)
180180

181181
def get_device(self, name: str) -> DeviceModel:
182-
return self._request_and_deserialize(f"/devices/{name}", DeviceModel)
182+
return self._request_v1(f"/devices/{name}", DeviceModel)
183183

184184
def get_state(self) -> WorkerState:
185-
return self._request_and_deserialize("/worker/state", WorkerState)
185+
return self._request_v1("/worker/state", WorkerState)
186186

187187
def set_state(
188188
self,
189189
state: Literal[WorkerState.RUNNING, WorkerState.PAUSED],
190190
defer: bool | None = False,
191191
):
192-
return self._request_and_deserialize(
192+
return self._request_v1(
193193
"/worker/state",
194194
target_type=WorkerState,
195195
method="PUT",
196196
data={"new_state": state, "defer": defer},
197197
)
198198

199199
def get_task(self, task_id: str) -> TrackableTask:
200-
return self._request_and_deserialize(f"/tasks/{task_id}", TrackableTask)
200+
return self._request_v1(f"/tasks/{task_id}", TrackableTask)
201201

202202
def get_all_tasks(self) -> TasksListResponse:
203-
return self._request_and_deserialize("/tasks", TasksListResponse)
203+
return self._request_v1("/tasks", TasksListResponse)
204204

205205
def get_active_task(self) -> WorkerTask:
206-
return self._request_and_deserialize("/worker/task", WorkerTask)
206+
return self._request_v1("/worker/task", WorkerTask)
207207

208208
def create_task(self, task: TaskRequest) -> TaskResponse:
209-
return self._request_and_deserialize(
209+
return self._request_v1(
210210
"/tasks",
211211
TaskResponse,
212212
method="POST",
@@ -215,12 +215,10 @@ def create_task(self, task: TaskRequest) -> TaskResponse:
215215
)
216216

217217
def clear_task(self, task_id: str) -> TaskResponse:
218-
return self._request_and_deserialize(
219-
f"/tasks/{task_id}", TaskResponse, method="DELETE"
220-
)
218+
return self._request_v1(f"/tasks/{task_id}", TaskResponse, method="DELETE")
221219

222220
def update_worker_task(self, task: WorkerTask) -> WorkerTask:
223-
return self._request_and_deserialize(
221+
return self._request_v1(
224222
"/worker/task",
225223
WorkerTask,
226224
method="PUT",
@@ -232,20 +230,18 @@ def cancel_current_task(
232230
state: Literal[WorkerState.ABORTING, WorkerState.STOPPING],
233231
reason: str | None = None,
234232
):
235-
return self._request_and_deserialize(
233+
return self._request_v1(
236234
"/worker/state",
237235
target_type=WorkerState,
238236
method="PUT",
239237
data={"new_state": state, "reason": reason},
240238
)
241239

242240
def get_environment(self) -> EnvironmentResponse:
243-
return self._request_and_deserialize("/environment", EnvironmentResponse)
241+
return self._request_v1("/environment", EnvironmentResponse)
244242

245243
def delete_environment(self) -> EnvironmentResponse:
246-
return self._request_and_deserialize(
247-
"/environment", EnvironmentResponse, method="DELETE"
248-
)
244+
return self._request_v1("/environment", EnvironmentResponse, method="DELETE")
249245

250246
def get_oidc_config(self) -> OIDCConfig | None:
251247
try:
@@ -257,12 +253,25 @@ def get_oidc_config(self) -> OIDCConfig | None:
257253
def get_python_environment(
258254
self, name: str | None = None, source: SourceInfo | None = None
259255
) -> PythonEnvironmentResponse:
260-
return self._request_and_deserialize(
256+
return self._request_v1(
261257
"/python_environment",
262258
PythonEnvironmentResponse,
263259
params={"name": name, "source": source},
264260
)
265261

262+
def _request_v1(
263+
self,
264+
suffix: str,
265+
target_type: type[T],
266+
data: Mapping[str, Any] | None = None,
267+
method="GET",
268+
get_exception: Callable[[requests.Response], Exception | None] = _exception,
269+
params: Mapping[str, Any] | None = None,
270+
) -> T:
271+
return self._request_and_deserialize(
272+
"/api/v1" + suffix, target_type, data, method, get_exception, params
273+
)
274+
266275
@start_as_current_span(TRACER, "method", "data", "suffix")
267276
def _request_and_deserialize(
268277
self,

tests/unit_tests/cli/test_cli.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def test_get_plans(runner: CliRunner):
160160

161161
response = responses.add(
162162
responses.GET,
163-
"http://localhost:8000/plans",
163+
"http://localhost:8000/api/v1/plans",
164164
json=PlanResponse(plans=[PlanModel.from_plan(plan)]).model_dump(),
165165
status=200,
166166
)
@@ -176,7 +176,7 @@ def test_get_devices(runner: CliRunner):
176176

177177
response = responses.add(
178178
responses.GET,
179-
"http://localhost:8000/devices",
179+
"http://localhost:8000/api/v1/devices",
180180
json=DeviceResponse(devices=[DeviceModel.from_device(device)]).model_dump(),
181181
status=200,
182182
)
@@ -218,7 +218,7 @@ def test_submit_plan(runner: CliRunner):
218218
}
219219

220220
response = responses.post(
221-
url="http://a.fake.host:12345/tasks",
221+
url="http://a.fake.host:12345/api/v1/tasks",
222222
match=[matchers.json_params_matcher(body_data)],
223223
)
224224

@@ -268,7 +268,7 @@ def test_submit_plan_without_stomp(runner: CliRunner):
268268
def test_run_plan(stomp_client: StompClient, runner: CliRunner):
269269
task_id = "abcd-1234"
270270
submit_response = responses.post(
271-
url="http://a.fake.host:12345/tasks",
271+
url="http://a.fake.host:12345/api/v1/tasks",
272272
match=[
273273
matchers.json_params_matcher(
274274
{
@@ -282,7 +282,7 @@ def test_run_plan(stomp_client: StompClient, runner: CliRunner):
282282
status=201,
283283
)
284284
run_response = responses.put(
285-
url="http://a.fake.host:12345/worker/task",
285+
url="http://a.fake.host:12345/api/v1/worker/task",
286286
match=[matchers.json_params_matcher({"task_id": task_id})],
287287
json={"task_id": task_id},
288288
)
@@ -398,7 +398,7 @@ def test_run_plan_feedback(
398398
@responses.activate
399399
def test_run_plan_background_without_stomp(runner: CliRunner):
400400
submit_response = responses.post(
401-
url="http://a.fake.host:12345/tasks",
401+
url="http://a.fake.host:12345/api/v1/tasks",
402402
match=[
403403
matchers.json_params_matcher(
404404
{
@@ -412,7 +412,7 @@ def test_run_plan_background_without_stomp(runner: CliRunner):
412412
status=201,
413413
)
414414
run_response = responses.put(
415-
url="http://a.fake.host:12345/worker/task",
415+
url="http://a.fake.host:12345/api/v1/worker/task",
416416
match=[matchers.json_params_matcher({"task_id": "abcd-1234"})],
417417
json={"task_id": "abcd-1234"},
418418
)
@@ -541,7 +541,7 @@ def test_get_env(runner: CliRunner):
541541
environment_id = uuid.uuid4()
542542
responses.add(
543543
responses.GET,
544-
"http://localhost:8000/environment",
544+
"http://localhost:8000/api/v1/environment",
545545
json=EnvironmentResponse(
546546
environment_id=environment_id, initialized=True
547547
).model_dump(mode="json"),
@@ -559,7 +559,10 @@ def test_get_env(runner: CliRunner):
559559
@responses.activate
560560
def test_get_state(runner: CliRunner):
561561
responses.add(
562-
responses.GET, "http://localhost:8000/worker/state", json="IDLE", status=200
562+
responses.GET,
563+
"http://localhost:8000/api/v1/worker/state",
564+
json="IDLE",
565+
status=200,
563566
)
564567
state = runner.invoke(main, ["controller", "state"])
565568
print(state.stderr)
@@ -576,7 +579,7 @@ def test_reset_env_client_behavior(
576579
environment_id = uuid.uuid4()
577580
responses.add(
578581
responses.DELETE,
579-
"http://localhost:8000/environment",
582+
"http://localhost:8000/api/v1/environment",
580583
json=EnvironmentResponse(
581584
environment_id=environment_id, initialized=False
582585
).model_dump(mode="json"),
@@ -588,7 +591,7 @@ def test_reset_env_client_behavior(
588591
for state in env_state:
589592
responses.add(
590593
responses.GET,
591-
"http://localhost:8000/environment",
594+
"http://localhost:8000/api/v1/environment",
592595
json=EnvironmentResponse(
593596
environment_id=environment_id, initialized=state
594597
).model_dump(mode="json"),
@@ -604,10 +607,10 @@ def test_reset_env_client_behavior(
604607
for index, call in enumerate(responses.calls):
605608
if index == 0:
606609
assert call.request.method == "DELETE"
607-
assert call.request.url == "http://localhost:8000/environment"
610+
assert call.request.url == "http://localhost:8000/api/v1/environment"
608611
else:
609612
assert call.request.method == "GET"
610-
assert call.request.url == "http://localhost:8000/environment"
613+
assert call.request.url == "http://localhost:8000/api/v1/environment"
611614

612615
# Check if the final environment status is printed correctly
613616
# assert "Environment is initialized." in result.output
@@ -625,7 +628,7 @@ def test_env_timeout(mock_sleep: Mock, runner: CliRunner):
625628
environment_id = uuid.uuid4()
626629
responses.add(
627630
responses.DELETE,
628-
"http://localhost:8000/environment",
631+
"http://localhost:8000/api/v1/environment",
629632
status=200,
630633
json=EnvironmentResponse(
631634
environment_id=environment_id, initialized=False
@@ -634,7 +637,7 @@ def test_env_timeout(mock_sleep: Mock, runner: CliRunner):
634637
# Add responses for each polling attempt, all indicating not initialized
635638
responses.add(
636639
responses.GET,
637-
"http://localhost:8000/environment",
640+
"http://localhost:8000/api/v1/environment",
638641
json=EnvironmentResponse(
639642
environment_id=environment_id, initialized=False
640643
).model_dump(mode="json"),
@@ -655,12 +658,12 @@ def test_env_timeout(mock_sleep: Mock, runner: CliRunner):
655658

656659
# First call should be DELETE
657660
assert responses.calls[0].request.method == "DELETE"
658-
assert responses.calls[0].request.url == "http://localhost:8000/environment"
661+
assert responses.calls[0].request.url == "http://localhost:8000/api/v1/environment"
659662

660663
# Remaining calls should all be GET
661664
for call in responses.calls[1:]: # Skip the first DELETE request # type: ignore
662665
assert call.request.method == "GET"
663-
assert call.request.url == "http://localhost:8000/environment"
666+
assert call.request.url == "http://localhost:8000/api/v1/environment"
664667

665668
# Check the output for the timeout message
666669
assert result.output == "Reloading environment\n"
@@ -673,7 +676,10 @@ def test_env_timeout(mock_sleep: Mock, runner: CliRunner):
673676
def test_env_reload_server_side_error(runner: CliRunner):
674677
# Setup mocked error response from the server
675678
responses.add(
676-
responses.DELETE, "http://localhost:8000/environment", status=500, json={}
679+
responses.DELETE,
680+
"http://localhost:8000/api/v1/environment",
681+
status=500,
682+
json={},
677683
)
678684

679685
result = runner.invoke(main, ["controller", "env", "-r"])
@@ -687,7 +693,7 @@ def test_env_reload_server_side_error(runner: CliRunner):
687693

688694
# Only call should be DELETE
689695
assert responses.calls[0].request.method == "DELETE"
690-
assert responses.calls[0].request.url == "http://localhost:8000/environment"
696+
assert responses.calls[0].request.url == "http://localhost:8000/api/v1/environment"
691697

692698
# Check the output for the timeout message
693699
# TODO this seems wrong but this is the current behaviour
@@ -1279,7 +1285,7 @@ def test_get_python_environment(runner: CliRunner):
12791285
}
12801286
response = responses.add(
12811287
responses.GET,
1282-
"http://localhost:8000/python_environment",
1288+
"http://localhost:8000/api/v1/python_environment",
12831289
json=scratch_config,
12841290
status=200,
12851291
)
@@ -1302,7 +1308,7 @@ def test_get_python_env_with_empty_response(runner: CliRunner):
13021308
}
13031309
response = responses.add(
13041310
responses.GET,
1305-
"http://localhost:8000/python_environment",
1311+
"http://localhost:8000/api/v1/python_environment",
13061312
json=scratch_config,
13071313
status=200,
13081314
)
@@ -1436,7 +1442,7 @@ def test_log_level_override(flag: str, level: str, runner: CliRunner):
14361442
def test_host_option(runner: CliRunner):
14371443
response = responses.add(
14381444
responses.GET,
1439-
"http://override.example.com:5678/plans",
1445+
"http://override.example.com:5678/api/v1/plans",
14401446
json={"plans": []},
14411447
status=200,
14421448
)
@@ -1454,7 +1460,7 @@ def test_host_overrides_config(runner: CliRunner):
14541460
config_path = "tests/unit_tests/example_yaml/rest_config.yaml"
14551461
response = responses.add(
14561462
responses.GET,
1457-
"http://override.example.com:5678/plans",
1463+
"http://override.example.com:5678/api/v1/plans",
14581464
json={"plans": []},
14591465
status=200,
14601466
)

tests/unit_tests/client/test_rest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def test_auth_request_functionality(
116116
environment_id = uuid.uuid4()
117117
mock_authn_server.stop() # Cannot use multiple RequestsMock context manager
118118
mock_get_env = mock_authn_server.get(
119-
"http://localhost:8000/environment",
119+
"http://localhost:8000/api/v1/environment",
120120
json=EnvironmentResponse(
121121
environment_id=environment_id, initialized=True
122122
).model_dump(mode="json"),
@@ -143,7 +143,7 @@ def test_refresh_if_signature_expired(
143143
environment_id = uuid.uuid4()
144144
mock_authn_server.stop() # Cannot use multiple RequestsMock context manager
145145
mock_get_env = mock_authn_server.get(
146-
"http://localhost:8000/environment",
146+
"http://localhost:8000/api/v1/environment",
147147
json=EnvironmentResponse(
148148
environment_id=environment_id, initialized=True
149149
).model_dump(mode="json"),

0 commit comments

Comments
 (0)