Coverage for src/fastapi_cloud_cli/commands/login.py: 100%

51 statements  

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

1import logging 1JKLMNOP

2from typing import Annotated, Any 1JKLMNOP

3 

4import typer 1JKLMNOP

5 

6from fastapi_cloud_cli._app import app, cloud_app 1JKLMNOP

7from fastapi_cloud_cli.api import APIClient 1JKLMNOP

8from fastapi_cloud_cli.commands._flow import ( 1JKLMNOP

9 DEFAULT_LOGIN_TIMEOUT_SECONDS, 

10 complete_device_login, 

11 device_authorization_output, 

12 render_login_output, 

13 start_device_authorization, 

14) 

15from fastapi_cloud_cli.commands.auth._app import auth_app 1JKLMNOP

16from fastapi_cloud_cli.utils.auth import Identity 1JKLMNOP

17from fastapi_cloud_cli.utils.cli import FastAPIRichToolkit, get_rich_toolkit 1JKLMNOP

18from fastapi_cloud_cli.utils.execution import JsonOutputOption 1JKLMNOP

19 

20logger = logging.getLogger(__name__) 1JKLMNOP

21 

22 

23def _interactive_login( 1JKLMNOP

24 toolkit: FastAPIRichToolkit, 

25 *, 

26 no_open: bool = False, 

27 timeout: int = DEFAULT_LOGIN_TIMEOUT_SECONDS, 

28) -> Any: 

29 with APIClient() as client: 1QohapCXRqibrDYSsjctEZTukdvF0UwlexG1VymfzH2WAngBI3

30 with toolkit.progress("Starting authorization", transient=True) as progress: 1QohapCXRqibrDYSsjctEZTukdvF0UwlexG1VymfzH2WAngBI3

31 with client.handle_http_errors(progress, toolkit=toolkit): 1QohapCXRqibrDYSsjctEZTukdvF0UwlexG1VymfzH2WAngBI3

32 authorization_data = start_device_authorization(client) 1QohapCXRqibrDYSsjctEZTukdvF0UwlexG1VymfzH2WAngBI3

33 

34 url = authorization_data.verification_uri_complete 1QohapCRqibrDSsjctETukdvFUwlexGVymfzHWAngBI

35 

36 if no_open: 1QohapCRqibrDSsjctETukdvFUwlexGVymfzHWAngBI

37 toolkit.print(f"Open {url}") 1opCqrDstEuvFwxGyzHABI

38 else: 

39 launch_cmd_res = typer.launch(url) 1QhaRibSjcTkdUleVmfWng

40 logger.debug(f"Launch command result: {launch_cmd_res}") 1QhaRibSjcTkdUleVmfWng

41 toolkit.print(f"Opening [link={url}]{url}[/link]") 1QhaRibSjcTkdUleVmfWng

42 

43 toolkit.print_line() 1QohapCRqibrDSsjctETukdvFUwlexGVymfzHWAngBI

44 

45 with toolkit.progress( 1QohapCRqibrDSsjctETukdvFUwlexGVymfzHWAngBI

46 "Waiting for user to authorize...", transient=True 

47 ) as progress: 

48 result = complete_device_login( 1QohapCRqibrDSsjctETukdvFUwlexGVymfzHWAngBI

49 client=client, 

50 progress=progress, 

51 toolkit=toolkit, 

52 device_code=authorization_data.device_code, 

53 interval=authorization_data.interval, 

54 timeout=timeout, 

55 cancel_hint="Run `fastapi cloud login` again to retry.", 

56 ) 

57 

58 toolkit.success( 1QohapRqibrSsjctTukdvUwlexVymfzWAngB

59 result, 

60 render_output=render_login_output, 

61 ) 

62 

63 

64@app.command() 1JKLMNOP

65@cloud_app.command() 1JKLMNOP

66@auth_app.command() 1JKLMNOP

67def login( 1JK`LM{NOP|

68 no_open: Annotated[ 

69 bool, 

70 typer.Option( 

71 "--no-open", 

72 help="Do not open the browser automatically.", 

73 ), 

74 ] = False, 

75 timeout: Annotated[ 

76 int, 

77 typer.Option( 

78 "--timeout", 

79 help="Maximum seconds to wait for authorization.", 

80 min=10, 

81 ), 

82 ] = DEFAULT_LOGIN_TIMEOUT_SECONDS, 

83 json_output: JsonOutputOption = False, 

84) -> Any: 

85 """ 

86 Login to FastAPI Cloud. 

87 """ 

88 if json_output: 1#o$hapC4X%'q(ibrD5Y)*s+jctE6Z,-u.kdvF70/:w;lexG81=?y@mfzH92[]A^ngBI!3_

89 with get_rich_toolkit(json_output=json_output, minimal=True) as toolkit: 1#$%'()*+,-./:;=?@[]^_

90 with APIClient() as client: 1#$%'()*+,-./:;=?@[]^_

91 with toolkit.progress( 1#$%'()*+,-./:;=?@[]^_

92 "Starting authorization", transient=True 

93 ) as progress: 

94 with client.handle_http_errors(progress, toolkit=toolkit): 1#$%'()*+,-./:;=?@[]^_

95 authorization_data = start_device_authorization(client) 1#$%'()*+,-./:;=?@[]^_

96 

97 toolkit.success(device_authorization_output(authorization_data)) 1#$%'()*+,-./:;=?@[]^_

98 

99 return 1#$%'()*+,-./:;=?@[]^_

100 identity = Identity() 1ohapC4XqibrD5YsjctE6ZukdvF70wlexG81ymfzH92AngBI!3

101 

102 with get_rich_toolkit() as toolkit: 1ohapC4XqibrD5YsjctE6ZukdvF70wlexG81ymfzH92AngBI!3

103 toolkit.print_title("Login to FastAPI Cloud", tag="FastAPI Cloud", emoji="🔐") 1ohapC4XqibrD5YsjctE6ZukdvF70wlexG81ymfzH92AngBI!3

104 toolkit.print_line() 1ohapC4XqibrD5YsjctE6ZukdvF70wlexG81ymfzH92AngBI!3

105 

106 if identity.is_logged_in(): 1ohapC4XqibrD5YsjctE6ZukdvF70wlexG81ymfzH92AngBI!3

107 toolkit.print("You are already logged in.") 1456789!

108 toolkit.print_line() 1456789!

109 toolkit.print( 1456789!

110 "Run [bold]fastapi cloud logout[/bold] first if you want to switch accounts.", 

111 emoji="💡", 

112 ) 

113 

114 return 1456789!

115 

116 if identity.has_deploy_token(): 1ohapCXqibrDYsjctEZukdvF0wlexG1ymfzH2AngBI3

117 toolkit.print( 1abcdefg

118 "You have [bold blue]FASTAPI_CLOUD_TOKEN[/] environment variable set.\n" 

119 "This token will take precedence over the user token for " 

120 "[blue]`fastapi deploy`[/] command.", 

121 emoji="⚠️", 

122 ) 

123 toolkit.print_line() 1abcdefg

124 

125 _interactive_login(toolkit, no_open=no_open, timeout=timeout) 1ohapCXqibrDYsjctEZukdvF0wlexG1ymfzH2AngBI3