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

1import logging 1abcdefg

2from typing import Annotated, Any 1abcdefg

3 

4import typer 1abcdefg

5from pydantic import BaseModel 1abcdefg

6from rich_toolkit import RichToolkit 1abcdefg

7 

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

14 

15logger = logging.getLogger(__name__) 1abcdefg

16 

17 

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

24 

25 

26class AppsUpdateOutput(BaseModel): 1abcdefg

27 app: UpdatedApp 1abcdefg

28 

29 

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

36 

37 return UpdatedApp.model_validate(response.json()) 1okphqlrismtnuj

38 

39 

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 ) 

46 

47 

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 """ 

72 

73 toolkit = get_user_command_context(ctx).toolkit 1ovCkpwDhqxElryFiszGmtAHnuBIj

74 

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 ) 

81 

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 ) 

87 

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 ) 

96 

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 ) 

113 

114 toolkit.success( 1okphqlrismtnuj

115 AppsUpdateOutput(app=app), 

116 render_output=_render_apps_update_output, 

117 )