Coverage for src/fastapi_cloud_cli/commands/apps/update.py: 100%
42 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-11-09 00:25 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-11-09 00:25 +0000
1import logging 1abcdefg
2from typing import Annotated, Any 1abcdefg
4import typer 1abcdefg
5from pydantic import BaseModel 1abcdefg
6from rich_toolkit import RichToolkit 1abcdefg
8from fastapi_cloud_cli.api import APIClient 1abcdefg
9from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context 1abcdefg
10from fastapi_cloud_cli.commands.apps._app import apps_app 1abcdefg
11from fastapi_cloud_cli.commands.deploy.archive import validate_app_directory 1abcdefg
12from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail 1abcdefg
13from fastapi_cloud_cli.utils.execution import JsonOutputOption 1abcdefg
15logger = logging.getLogger(__name__) 1abcdefg
18class UpdatedApp(BaseModel): 1abcdefg
19 id: str 1abcdefg
20 team_id: str 1abcdefg
21 slug: str 1abcdefg
22 name: str 1abcdefg
23 directory: str | None 1abcdefg
26class AppsUpdateOutput(BaseModel): 1abcdefg
27 app: UpdatedApp 1abcdefg
30def _update_app(client: APIClient, *, app_id: str, directory: str | None) -> UpdatedApp: 1abcdefg
31 response = client.patch( 1okphqlrismtnuj
32 f"/apps/{app_id}",
33 json={"directory": directory},
34 )
35 response.raise_for_status() 1okphqlrismtnuj
37 return UpdatedApp.model_validate(response.json()) 1okphqlrismtnuj
40def _render_apps_update_output(data: AppsUpdateOutput, toolkit: RichToolkit) -> None: 1abhcdiefgj
41 toolkit.print(f"Updated app [bold]{data.app.name}[/bold]", bullet=False) 1khlimnj
42 toolkit.print( 1khlimnj
43 f"Directory: [bold]{data.app.directory if data.app.directory is not None else '.'}[/bold]",
44 bullet=False,
45 )
48@apps_app.command("update", cls=UserCommand) 1abcdefg
49def update_app( 1abJcdKefgL
50 ctx: typer.Context,
51 app_id: Annotated[
52 str | None,
53 typer.Argument(
54 help="ID of the app to update (defaults to the app linked to the current directory).",
55 ),
56 ] = None,
57 directory: Annotated[
58 str | None,
59 typer.Option(
60 "--directory",
61 help=(
62 "Relative app directory containing the pyproject.toml "
63 "(for example: src or backend)."
64 ),
65 ),
66 ] = None,
67 json_output: JsonOutputOption = False,
68) -> Any:
69 """
70 Update FastAPI Cloud app metadata.
71 """
73 toolkit = get_user_command_context(ctx).toolkit 1ovCkpwDhqxElryFiszGmtAHnuBIj
75 if directory is None: 1ovCkpwDhqxElryFiszGmtAHnuBIj
76 toolkit.fail( 1CDEFGHI
77 "missing_required_input",
78 "No updates provided.",
79 hint="Pass --directory to update the app directory.",
80 )
82 target_app_id = resolve_app_id_or_fail( 1ovkpwhqxlryiszmtAnuBj
83 toolkit,
84 app_id=app_id,
85 hint="Pass an app ID or run `fastapi cloud apps create --link` first.",
86 )
88 try: 1ovkpwhqxlryiszmtAnuBj
89 directory = validate_app_directory(directory) 1ovkpwhqxlryiszmtAnuBj
90 except ValueError as e: 1vwxyzAB
91 toolkit.fail( 1vwxyzAB
92 "invalid_input",
93 f"Invalid app directory: {e}",
94 hint="Pass a relative app directory such as `src` or `backend`.",
95 )
97 with APIClient() as client: 1okphqlrismtnuj
98 with toolkit.progress( 1okphqlrismtnuj
99 title="Updating app",
100 transient=True,
101 ) as progress:
102 with client.handle_http_errors( 1okphqlrismtnuj
103 progress,
104 default_message="Error updating app. Please try again later.",
105 not_found_message="App not found.",
106 toolkit=toolkit,
107 ):
108 app = _update_app( 1okphqlrismtnuj
109 client,
110 app_id=target_app_id,
111 directory=directory,
112 )
114 toolkit.success( 1okphqlrismtnuj
115 AppsUpdateOutput(app=app),
116 render_output=_render_apps_update_output,
117 )