Skip to content

Commit 66a07c4

Browse files
authored
Merge pull request fastapi#38 from fastapilabs/09-17-_always_fetch_app_and_team_fix_dashboard_url
♻️ Always fetch app and team, fix dashboard url
2 parents 62888bc + 520a283 commit 66a07c4

File tree

2 files changed

+112
-21
lines changed

2 files changed

+112
-21
lines changed

src/fastapi_cli/commands/deploy.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from enum import Enum
77
from itertools import cycle
88
from pathlib import Path
9-
from typing import Any, Dict, List, Optional, Tuple, Union
9+
from typing import Any, Dict, List, Optional, Union
1010

1111
import rignore
1212
import typer
@@ -163,6 +163,16 @@ def _get_apps(team_slug: str) -> List[AppResponse]:
163163
return [AppResponse.model_validate(app) for app in data]
164164

165165

166+
def _get_team(team_id: str) -> Team:
167+
with APIClient() as client:
168+
response = client.get(f"/teams/{team_id}")
169+
response.raise_for_status()
170+
171+
data = response.json()
172+
173+
return Team.model_validate(data)
174+
175+
166176
class DeploymentResponse(BaseModel):
167177
id: str
168178
app_id: str
@@ -202,9 +212,7 @@ def _get_deployment(app_id: str, deployment_id: str) -> DeploymentResponse:
202212
]
203213

204214

205-
def _configure_app(
206-
toolkit: RichToolkit, path_to_deploy: Path
207-
) -> Tuple[AppConfig, AppResponse]:
215+
def _configure_app(toolkit: RichToolkit, path_to_deploy: Path) -> AppConfig:
208216
if not toolkit.confirm(f"Setup and deploy [blue]{path_to_deploy}[/]?", tag="dir"):
209217
raise typer.Exit(0)
210218

@@ -271,7 +279,7 @@ def _configure_app(
271279

272280
write_app_config(path_to_deploy, app_config)
273281

274-
return app_config, app
282+
return app_config
275283

276284

277285
def _wait_for_deployment(
@@ -348,33 +356,31 @@ def deploy(
348356
path_to_deploy = path or Path.cwd()
349357

350358
app_config = get_app_config(path_to_deploy)
351-
app_data: Optional[AppResponse] = None
352359

353360
if not app_config:
354-
app_config, app_data = _configure_app(
355-
toolkit, path_to_deploy=path_to_deploy
356-
)
361+
app_config = _configure_app(toolkit, path_to_deploy=path_to_deploy)
357362
else:
358363
toolkit.print("Deploying app...")
359364
toolkit.print_line()
360365

361-
with toolkit.progress("Checking app...", transient=True) as progress:
362-
app_data = _get_app(app_config.app_id)
366+
with toolkit.progress("Checking app...", transient=True) as progress:
367+
app = _get_app(app_config.app_id)
368+
team = _get_team(app_config.team_id)
363369

364-
if not app_data:
365-
progress.set_error(
366-
"App not found. Make sure you're logged in the correct account."
367-
)
370+
if not app:
371+
progress.set_error(
372+
"App not found. Make sure you're logged in the correct account."
373+
)
368374

369-
raise typer.Exit(1)
375+
raise typer.Exit(1)
370376

371377
toolkit.print_line()
372378

373379
archive_path = archive(path or Path.cwd()) # noqa: F841
374380

375381
with toolkit.progress(title="Creating deployment") as progress:
376382
with handle_http_errors(progress):
377-
deployment = _create_deployment(app_data.id)
383+
deployment = _create_deployment(app.id)
378384

379385
progress.log(
380386
f"Deployment created successfully! Deployment slug: {deployment.slug}"
@@ -390,13 +396,11 @@ def deploy(
390396

391397
check_deployment_url = (
392398
settings.base_frontend_url
393-
+ f"/apps/{app_data.slug}/deployments/{deployment.id}"
399+
+ f"/{team.slug}/apps/{app.slug}/deployments/{deployment.id}"
394400
)
395401

396402
if not skip_wait:
397-
_wait_for_deployment(
398-
toolkit, app_data.id, deployment.id, check_deployment_url
399-
)
403+
_wait_for_deployment(toolkit, app.id, deployment.id, check_deployment_url)
400404
else:
401405
toolkit.print(
402406
f"Check the status of your deployment at [link]{check_deployment_url}[/link]"

tests/test_cli_deploy.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ def test_creates_and_uploads_deployment_then_fails(
247247
)
248248
)
249249

250+
respx_mock.get("/apps/1234").mock(
251+
return_value=Response(200, json={"slug": "demo", "id": "1234"})
252+
)
253+
254+
respx_mock.get("/teams/123").mock(
255+
return_value=Response(
256+
200,
257+
json={
258+
"name": "team1",
259+
"slug": "team1",
260+
"id": "123",
261+
},
262+
)
263+
)
264+
250265
respx_mock.post(
251266
"/apps/1234/deployments/",
252267
).mock(
@@ -326,6 +341,21 @@ def test_exists_successfully_when_deployment_is_done(
326341
)
327342
)
328343

344+
respx_mock.get("/apps/1234").mock(
345+
return_value=Response(200, json={"slug": "demo", "id": "1234"})
346+
)
347+
348+
respx_mock.get("/teams/123").mock(
349+
return_value=Response(
350+
200,
351+
json={
352+
"name": "team1",
353+
"slug": "team1",
354+
"id": "123",
355+
},
356+
)
357+
)
358+
329359
respx_mock.post(
330360
"/apps/1234/deployments/",
331361
).mock(
@@ -395,6 +425,19 @@ def test_exists_successfully_when_deployment_is_done_when_app_is_configured(
395425
return_value=Response(200, json={"slug": "demo", "id": "1234"})
396426
)
397427

428+
respx_mock.get(
429+
"/teams/123",
430+
).mock(
431+
return_value=Response(
432+
200,
433+
json={
434+
"name": "team1",
435+
"slug": "team1",
436+
"id": "123",
437+
},
438+
)
439+
)
440+
398441
respx_mock.post(
399442
"/apps/1234/deployments/",
400443
).mock(
@@ -459,6 +502,16 @@ def test_shows_error_when_app_does_not_exist(
459502
config_path.write_text('{"app_id": "some-random-id", "team_id": "123"}')
460503

461504
respx_mock.get("/apps/some-random-id").mock(return_value=Response(404))
505+
respx_mock.get("/teams/123").mock(
506+
return_value=Response(
507+
200,
508+
json={
509+
"name": "team1",
510+
"slug": "team1",
511+
"id": "123",
512+
},
513+
)
514+
)
462515

463516
with changing_dir(tmp_path):
464517
result = runner.invoke(app, ["deploy"])
@@ -468,6 +521,26 @@ def test_shows_error_when_app_does_not_exist(
468521
assert "App not found" in result.output
469522

470523

524+
@pytest.mark.respx(base_url=settings.base_api_url)
525+
def test_returns_error_when_team_not_found(
526+
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
527+
) -> None:
528+
config_path = tmp_path / ".fastapi" / "cloud.json"
529+
530+
config_path.parent.mkdir(parents=True, exist_ok=True)
531+
config_path.write_text('{"app_id": "1234", "team_id": "some-random-id"}')
532+
533+
respx_mock.get("/apps/1234").mock(
534+
return_value=Response(200, json={"slug": "demo", "id": "1234"})
535+
)
536+
respx_mock.get("/teams/some-random-id").mock(return_value=Response(404))
537+
538+
with changing_dir(tmp_path):
539+
result = runner.invoke(app, ["deploy"])
540+
541+
assert result.exit_code == 1
542+
543+
471544
@pytest.mark.respx(base_url=settings.base_api_url)
472545
def test_can_skip_waiting(
473546
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
@@ -491,6 +564,20 @@ def test_can_skip_waiting(
491564
)
492565
)
493566

567+
respx_mock.get("/apps/1234").mock(
568+
return_value=Response(200, json={"slug": "demo", "id": "1234"})
569+
)
570+
respx_mock.get("/teams/123").mock(
571+
return_value=Response(
572+
200,
573+
json={
574+
"name": "team1",
575+
"slug": "team1",
576+
"id": "123",
577+
},
578+
)
579+
)
580+
494581
respx_mock.post(
495582
"/apps/1234/deployments/",
496583
).mock(

0 commit comments

Comments
 (0)