Skip to content

Commit 93f6015

Browse files
authored
♻️ Use new deployment route to fetch deployment (#266)
1 parent 0b529f9 commit 93f6015

6 files changed

Lines changed: 38 additions & 54 deletions

File tree

src/fastapi_cloud_cli/commands/deploy/wait.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
def _verify_deployment(
4747
toolkit: RichToolkit,
4848
client: APIClient,
49-
app_id: str,
5049
deployment: CreateDeploymentResponse,
5150
) -> None:
5251
failed_status: str | None = None
@@ -57,7 +56,7 @@ def _verify_deployment(
5756
done_emoji="✅",
5857
) as progress:
5958
try:
60-
final_status = client.poll_deployment_status(app_id, deployment.id)
59+
final_status = client.poll_deployment_status(deployment.id)
6160
except (TimeoutError, TooManyRetriesError, StreamLogError):
6261
progress.metadata["done_emoji"] = "⚠️"
6362
progress.current_message = (
@@ -168,6 +167,4 @@ def _wait_for_deployment(
168167
if build_complete:
169168
toolkit.print_line()
170169

171-
_verify_deployment(
172-
toolkit=toolkit, client=client, app_id=app_id, deployment=deployment
173-
)
170+
_verify_deployment(toolkit=toolkit, client=client, deployment=deployment)

src/fastapi_cloud_cli/commands/deployments.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,8 @@ def _get_deployments(
9595
)
9696

9797

98-
def _get_deployment(
99-
client: APIClient, *, app_id: str, deployment_id: str
100-
) -> DeploymentGetOutput:
101-
response = client.get(f"/apps/{app_id}/deployments/{deployment_id}")
98+
def _get_deployment(client: APIClient, *, deployment_id: str) -> DeploymentGetOutput:
99+
response = client.get(f"/deployments/{deployment_id}")
102100
response.raise_for_status()
103101

104102
return DeploymentGetOutput(deployment=Deployment.model_validate(response.json()))
@@ -344,7 +342,7 @@ def get_deployment(
344342
hint="Run `fastapi cloud login` or set FASTAPI_CLOUD_TOKEN.",
345343
)
346344

347-
target_app_id = resolve_app_id_or_fail(toolkit, app_id=app_id)
345+
resolve_app_id_or_fail(toolkit, app_id=app_id)
348346

349347
with APIClient() as client:
350348
with toolkit.progress(
@@ -359,7 +357,6 @@ def get_deployment(
359357
):
360358
result = _get_deployment(
361359
client,
362-
app_id=target_app_id,
363360
deployment_id=deployment_id,
364361
)
365362

src/fastapi_cloud_cli/utils/api.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,6 @@ def stream_app_logs(
487487

488488
def poll_deployment_status(
489489
self,
490-
app_id: str,
491490
deployment_id: str,
492491
) -> DeploymentStatus:
493492
start = time.monotonic()
@@ -498,7 +497,7 @@ def poll_deployment_status(
498497
raise TimeoutError("Deployment verification timed out")
499498

500499
with attempt(error_count):
501-
response = self.get(f"/apps/{app_id}/deployments/{deployment_id}")
500+
response = self.get(f"/deployments/{deployment_id}")
502501
response.raise_for_status()
503502
status = DeploymentStatus(response.json()["status"])
504503
error_count = 0

tests/test_api_client.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -389,19 +389,12 @@ def responses(request: httpx.Request, route: respx.Route) -> Response:
389389

390390

391391
@pytest.fixture
392-
def app_id() -> str:
393-
return "test-app-456"
394-
395-
396-
@pytest.fixture
397-
def poll_route(
398-
respx_mock: respx.MockRouter, app_id: str, deployment_id: str
399-
) -> respx.Route:
400-
return respx_mock.get(f"/apps/{app_id}/deployments/{deployment_id}")
392+
def poll_route(respx_mock: respx.MockRouter, deployment_id: str) -> respx.Route:
393+
return respx_mock.get(f"/deployments/{deployment_id}")
401394

402395

403396
def test_poll_deployment_status_recovers_from_transient_errors(
404-
poll_route: respx.Route, client: APIClient, app_id: str, deployment_id: str
397+
poll_route: respx.Route, client: APIClient, deployment_id: str
405398
) -> None:
406399
call_count = 0
407400

@@ -415,26 +408,24 @@ def handler(request: httpx.Request, route: respx.Route) -> Response:
415408
poll_route.mock(side_effect=handler)
416409

417410
with patch("time.sleep"):
418-
status = client.poll_deployment_status(app_id, deployment_id)
411+
status = client.poll_deployment_status(deployment_id)
419412

420413
assert status == DeploymentStatus.success
421414
assert call_count == 3
422415

423416

424417
def test_poll_deployment_status_raises_after_max_consecutive_errors(
425-
poll_route: respx.Route, client: APIClient, app_id: str, deployment_id: str
418+
poll_route: respx.Route, client: APIClient, deployment_id: str
426419
) -> None:
427420
poll_route.mock(return_value=Response(500))
428421

429422
with patch("time.sleep"), pytest.raises(TooManyRetriesError):
430-
client.poll_deployment_status(app_id, deployment_id)
423+
client.poll_deployment_status(deployment_id)
431424

432425

433-
def test_poll_deployment_status_timeout(
434-
client: APIClient, app_id: str, deployment_id: str
435-
) -> None:
426+
def test_poll_deployment_status_timeout(client: APIClient, deployment_id: str) -> None:
436427
with (
437428
patch("fastapi_cloud_cli.utils.api.POLL_TIMEOUT", timedelta(seconds=-1)),
438429
pytest.raises(TimeoutError, match="timed out"),
439430
):
440-
client.poll_deployment_status(app_id, deployment_id)
431+
client.poll_deployment_status(deployment_id)

tests/test_cli_deploy.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ def test_updates_app_directory_via_api_when_changed(
844844
)
845845
)
846846

847-
respx_mock.get(f"/apps/{app_data['id']}/deployments/{deployment_data['id']}").mock(
847+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
848848
return_value=Response(200, json={**deployment_data, "status": "success"})
849849
)
850850

@@ -917,7 +917,7 @@ def test_does_not_update_app_directory_when_unchanged(
917917
)
918918
)
919919

920-
respx_mock.get(f"/apps/{app_data['id']}/deployments/{deployment_data['id']}").mock(
920+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
921921
return_value=Response(200, json={**deployment_data, "status": "success"})
922922
)
923923

@@ -999,7 +999,7 @@ def test_exits_successfully_when_deployment_is_done(
999999
)
10001000
)
10011001

1002-
respx_mock.get(f"/apps/{app_data['id']}/deployments/{deployment_data['id']}").mock(
1002+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
10031003
return_value=Response(200, json={**deployment_data, "status": "success"})
10041004
)
10051005

@@ -1065,7 +1065,7 @@ def test_exits_successfully_when_deployment_is_done_when_app_is_configured(
10651065
)
10661066
)
10671067

1068-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
1068+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
10691069
return_value=Response(200, json={**deployment_data, "status": "success"})
10701070
)
10711071

@@ -1497,7 +1497,7 @@ def build_logs_handler(request: httpx.Request, route: respx.Route) -> Response:
14971497
side_effect=build_logs_handler
14981498
)
14991499

1500-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
1500+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
15011501
return_value=Response(200, json={**deployment_data, "status": "success"})
15021502
)
15031503

@@ -1581,7 +1581,7 @@ def build_logs_handler(request: httpx.Request, route: respx.Route) -> Response:
15811581
side_effect=build_logs_handler
15821582
)
15831583

1584-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
1584+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
15851585
return_value=Response(200, json={**deployment_data, "status": "success"})
15861586
)
15871587

@@ -1733,7 +1733,7 @@ def test_deploy_successfully_with_token(
17331733
)
17341734

17351735
respx_mock.get(
1736-
f"/apps/{app_id}/deployments/{deployment_data['id']}",
1736+
f"/deployments/{deployment_data['id']}",
17371737
headers={"Authorization": "Bearer hello"},
17381738
).mock(return_value=Response(200, json={**deployment_data, "status": "success"}))
17391739

@@ -1856,7 +1856,7 @@ def test_upload_deployment_progress(
18561856
),
18571857
)
18581858
)
1859-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_id}").mock(
1859+
respx_mock.get(f"/deployments/{deployment_id}").mock(
18601860
return_value=Response(200, json={**deployment_data, "status": "success"})
18611861
)
18621862

@@ -1919,7 +1919,7 @@ def test_deploy_with_app_id_arg(
19191919
)
19201920
)
19211921

1922-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
1922+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
19231923
return_value=Response(200, json={**deployment_data, "status": "success"})
19241924
)
19251925

@@ -1971,7 +1971,7 @@ def test_deploy_with_app_id_from_env_var(
19711971
)
19721972
)
19731973

1974-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
1974+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
19751975
return_value=Response(200, json={**deployment_data, "status": "success"})
19761976
)
19771977

@@ -2028,7 +2028,7 @@ def test_deploy_with_app_id_matching_local_config(
20282028
)
20292029
)
20302030

2031-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2031+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
20322032
return_value=Response(200, json={**deployment_data, "status": "success"})
20332033
)
20342034

@@ -2186,7 +2186,7 @@ def test_verification_failure_after_build_complete(
21862186

21872187
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
21882188

2189-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2189+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
21902190
return_value=Response(
21912191
200, json={**deployment_data, "status": "verifying_failed"}
21922192
)
@@ -2221,7 +2221,7 @@ def poll_handler(request: httpx.Request, route: respx.Route) -> Response:
22212221
return Response(200, json={**deployment_data, "status": "verifying"})
22222222
return Response(200, json={**deployment_data, "status": "success"})
22232223

2224-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2224+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
22252225
side_effect=poll_handler
22262226
)
22272227

@@ -2268,7 +2268,7 @@ def test_verifying_skipped_treated_as_success(
22682268

22692269
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
22702270

2271-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2271+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
22722272
return_value=Response(
22732273
200, json={**deployment_data, "status": "verifying_skipped"}
22742274
)
@@ -2369,7 +2369,7 @@ def test_large_file_threshold_warning(
23692369
deployment_data = _get_random_deployment(app_id=app_id)
23702370

23712371
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
2372-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2372+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
23732373
return_value=Response(200, json={**deployment_data, "status": "success"})
23742374
)
23752375

@@ -2397,7 +2397,7 @@ def test_large_file_threshold_only_top_three_files_with_more_indicator(
23972397
deployment_data = _get_random_deployment(app_id=app_id)
23982398

23992399
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
2400-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2400+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
24012401
return_value=Response(200, json={**deployment_data, "status": "success"})
24022402
)
24032403

@@ -2429,7 +2429,7 @@ def test_large_file_threshold_does_not_warn_when_no_large_files(
24292429
deployment_data = _get_random_deployment(app_id=app_id)
24302430

24312431
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
2432-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2432+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
24332433
return_value=Response(200, json={**deployment_data, "status": "success"})
24342434
)
24352435

@@ -2455,7 +2455,7 @@ def test_large_file_threshold_custom_threshold(
24552455
deployment_data = _get_random_deployment(app_id=app_id)
24562456

24572457
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
2458-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2458+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
24592459
return_value=Response(200, json={**deployment_data, "status": "success"})
24602460
)
24612461

@@ -2480,7 +2480,7 @@ def test_large_file_threshold_custom_threshold_envvar(
24802480
deployment_data = _get_random_deployment(app_id=app_id)
24812481

24822482
_setup_deployment_mocks(respx_mock, app_id, team_id, deployment_data, tmp_path)
2483-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2483+
respx_mock.get(f"/deployments/{deployment_data['id']}").mock(
24842484
return_value=Response(200, json={**deployment_data, "status": "success"})
24852485
)
24862486

tests/test_cli_deployments.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def test_gets_deployment_as_json_with_app_id(
232232
"url": "https://api.fastapicloud.app",
233233
"dashboard_url": "https://dashboard.fastapicloud.com/acme/apps/api/deployments/api-20260522",
234234
}
235-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment['id']}").mock(
235+
respx_mock.get(f"/deployments/{deployment['id']}").mock(
236236
return_value=Response(200, json=deployment)
237237
)
238238

@@ -268,9 +268,9 @@ def test_gets_deployment_as_json_uses_linked_app(
268268
"url": "https://api.fastapicloud.app",
269269
"dashboard_url": "https://dashboard.fastapicloud.com/acme/apps/api/deployments/api-20260522",
270270
}
271-
respx_mock.get(
272-
f"/apps/{configured_app.app_id}/deployments/{deployment['id']}"
273-
).mock(return_value=Response(200, json=deployment))
271+
respx_mock.get(f"/deployments/{deployment['id']}").mock(
272+
return_value=Response(200, json=deployment)
273+
)
274274

275275
with changing_dir(configured_app.path):
276276
result = runner.invoke(app, ["deployments", "get", deployment["id"], "--json"])
@@ -315,7 +315,7 @@ def test_gets_deployment_in_human_output(
315315
"url": "https://api.fastapicloud.app",
316316
"dashboard_url": "https://dashboard.example.com/d/api-20260522",
317317
}
318-
respx_mock.get(f"/apps/{app_id}/deployments/{deployment['id']}").mock(
318+
respx_mock.get(f"/deployments/{deployment['id']}").mock(
319319
return_value=Response(200, json=deployment)
320320
)
321321

0 commit comments

Comments
 (0)