Coverage for src/fastapi_cloud_cli/commands/tokens/list.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-11-09 00:25 +0000

1from typing import Annotated, Any 1abcdefg

2 

3import typer 1abcdefg

4from pydantic import BaseModel 1abcdefg

5from rich.table import Table 1abcdefg

6from rich.text import Text 1abcdefg

7from rich_toolkit import RichToolkit 1abcdefg

8 

9from fastapi_cloud_cli.api import APIClient 1abcdefg

10from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context 1abcdefg

11from fastapi_cloud_cli.commands.tokens._app import tokens_app 1abcdefg

12from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail 1abcdefg

13from fastapi_cloud_cli.utils.execution import JsonOutputOption 1abcdefg

14 

15 

16class DeployToken(BaseModel): 1abcdefg

17 id: str 1abcdefg

18 name: str 1abcdefg

19 created_at: str 1abcdefg

20 expired_at: str 1abcdefg

21 

22 

23class DeployTokensListAPIResponse(BaseModel): 1abcdefg

24 data: list[DeployToken] 1abcdefg

25 

26 

27class DeployTokensListOutput(BaseModel): 1abcdefg

28 app_id: str 1abcdefg

29 tokens: list[DeployToken] 1abcdefg

30 

31 

32def _get_deploy_tokens(client: APIClient, app_id: str) -> DeployTokensListAPIResponse: 1abcdefg

33 response = client.get(f"/apps/{app_id}/tokens") 1vwhrxyiozAjsBCkpDEltFGmuHInq

34 response.raise_for_status() 1vwhrxyiozAjsBCkpDEltFGmuHInq

35 

36 return DeployTokensListAPIResponse.model_validate(response.json()) 1vwhrxyiozAjsBCkpDEltFGmuHInq

37 

38 

39def _get_deploy_tokens_table(tokens: list[DeployToken]) -> Table: 1abcdefg

40 table = Table.grid(padding=(0, 2), pad_edge=False) 1hijklmn

41 table.add_column("Name", no_wrap=True) 1hijklmn

42 table.add_column("Expiration", no_wrap=True) 1hijklmn

43 table.add_column("ID", no_wrap=True, overflow="ignore") 1hijklmn

44 table.add_row( 1hijklmn

45 Text("Name", style="bold"), 

46 Text("Expiration", style="bold"), 

47 Text("ID", style="bold"), 

48 ) 

49 table.add_row("", "", "") 1hijklmn

50 

51 for token in tokens: 1hijklmn

52 table.add_row( 1hijklmn

53 Text(token.name), 

54 Text(token.expired_at[:10], style="dim"), 

55 Text(token.id), 

56 ) 

57 

58 return table 1hijklmn

59 

60 

61def _render_deploy_tokens_list_output( 1abocdpefgq

62 data: DeployTokensListOutput, toolkit: RichToolkit 

63) -> None: 

64 toolkit.print_title("deploy tokens") 1hriojskpltmunq

65 toolkit.print_line() 1hriojskpltmunq

66 

67 if not data.tokens: 1hriojskpltmunq

68 toolkit.print("No deploy tokens found.", bullet=False) 1rosptuq

69 return 1rosptuq

70 

71 toolkit.print(_get_deploy_tokens_table(data.tokens), bullet=False) 1hijklmn

72 

73 

74@tokens_app.command("list", cls=UserCommand) 1abcdefg

75def list_tokens( 1abQcdRefgS

76 ctx: typer.Context, 

77 app_id: Annotated[ 

78 str | None, 

79 typer.Option( 

80 "--app-id", 

81 help="ID of the app whose deploy tokens should be listed.", 

82 ), 

83 ] = None, 

84 json_output: JsonOutputOption = False, 

85) -> Any: 

86 """ 

87 List deploy tokens for an app. 

88 """ 

89 

90 toolkit = get_user_command_context(ctx).toolkit 1vwhrJxyioKzAjsLBCkpMDEltNFGmuOHInqP

91 

92 target_app_id = resolve_app_id_or_fail(toolkit, app_id=app_id) 1vwhrJxyioKzAjsLBCkpMDEltNFGmuOHInqP

93 

94 with APIClient() as client: 1vwhrxyiozAjsBCkpDEltFGmuHInq

95 with toolkit.progress( 1vwhrxyiozAjsBCkpDEltFGmuHInq

96 title="Fetching deploy tokens", 

97 transient=True, 

98 ) as progress: 

99 with client.handle_http_errors( 1vwhrxyiozAjsBCkpDEltFGmuHInq

100 progress, 

101 default_message="Error fetching deploy tokens. Please try again later.", 

102 not_found_message="App not found.", 

103 toolkit=toolkit, 

104 ): 

105 tokens = _get_deploy_tokens(client=client, app_id=target_app_id) 1vwhrxyiozAjsBCkpDEltFGmuHInq

106 

107 toolkit.success( 1vwhrxyiozAjsBCkpDEltFGmuHInq

108 DeployTokensListOutput(app_id=target_app_id, tokens=tokens.data), 

109 render_output=_render_deploy_tokens_list_output, 

110 )