Skip to content

Commit f26b653

Browse files
authored
Merge branch 'main' into include-hidden-files
2 parents ed193d3 + e3150eb commit f26b653

File tree

5 files changed

+85
-5
lines changed

5 files changed

+85
-5
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dependencies = [
3838
"rich-toolkit >= 0.14.5",
3939
"pydantic[email] >= 1.6.1",
4040
"sentry-sdk >= 2.20.0",
41+
"fastar >= 0.5.0",
4142
]
4243

4344
[project.optional-dependencies]

release-notes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
* ♻️ Clean up code archives after uploading. PR [#106](https://github.com/fastapilabs/fastapi-cloud-cli/pull/106) by [@DoctorJohn](https://github.com/DoctorJohn).
1414

15+
### Refactors
16+
17+
* 🧑‍💻 Handle already logged in state. PR [#103](https://github.com/fastapilabs/fastapi-cloud-cli/pull/103) by [@alejsdev](https://github.com/alejsdev).
18+
* ⚡️ Speed up archive creation. PR [#111](https://github.com/fastapilabs/fastapi-cloud-cli/pull/111) by [@DoctorJohn](https://github.com/DoctorJohn).
19+
1520
## 0.3.1
1621

1722
### Fixes

src/fastapi_cloud_cli/commands/deploy.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
import json
33
import logging
44
import subprocess
5-
import tarfile
65
import tempfile
76
import time
87
from enum import Enum
98
from itertools import cycle
109
from pathlib import Path
1110
from typing import Any, Dict, Generator, List, Optional, Union
1211

12+
import fastar
1313
import rignore
1414
import typer
1515
from httpx import Client
@@ -65,13 +65,14 @@ def archive(path: Path, tar_path: Path) -> Path:
6565
logger.debug("Archive will be created at: %s", tar_path)
6666

6767
file_count = 0
68-
with tarfile.open(tar_path, "w") as tar:
68+
with fastar.open(tar_path, "w") as tar:
6969
for filename in files:
7070
if filename.is_dir():
7171
continue
7272

73-
logger.debug("Adding %s to archive", filename.relative_to(path))
74-
tar.add(filename, arcname=filename.relative_to(path))
73+
arcname = filename.relative_to(path)
74+
logger.debug("Adding %s to archive", arcname)
75+
tar.append(filename, arcname=arcname)
7576
file_count += 1
7677

7778
logger.debug("Archive created successfully with %s files", file_count)

src/fastapi_cloud_cli/commands/login.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88

99
from fastapi_cloud_cli.config import Settings
1010
from fastapi_cloud_cli.utils.api import APIClient
11-
from fastapi_cloud_cli.utils.auth import AuthConfig, write_auth_config
11+
from fastapi_cloud_cli.utils.auth import (
12+
AuthConfig,
13+
get_auth_token,
14+
is_logged_in,
15+
is_token_expired,
16+
write_auth_config,
17+
)
1218
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
1319

1420
logger = logging.getLogger(__name__)
@@ -76,6 +82,20 @@ def login() -> Any:
7682
"""
7783
Login to FastAPI Cloud. 🚀
7884
"""
85+
token = get_auth_token()
86+
if token is not None and is_token_expired(token):
87+
with get_rich_toolkit(minimal=True) as toolkit:
88+
toolkit.print("Your session has expired. Logging in again...")
89+
toolkit.print_line()
90+
91+
if is_logged_in():
92+
with get_rich_toolkit(minimal=True) as toolkit:
93+
toolkit.print("You are already logged in.")
94+
toolkit.print(
95+
"Run [bold]fastapi logout[/bold] first if you want to switch accounts."
96+
)
97+
return
98+
7999
with get_rich_toolkit() as toolkit, APIClient() as client:
80100
toolkit.print_title("Login to FastAPI Cloud", tag="FastAPI")
81101

tests/test_cli_login.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import time
12
from pathlib import Path
23
from unittest.mock import patch
34

@@ -9,6 +10,7 @@
910

1011
from fastapi_cloud_cli.cli import app
1112
from fastapi_cloud_cli.config import Settings
13+
from tests.utils import create_jwt_token
1214

1315
runner = CliRunner()
1416
settings = Settings.get()
@@ -162,3 +164,54 @@ def test_fetch_access_token_handles_500_error(respx_mock: respx.MockRouter) -> N
162164
with APIClient() as client:
163165
with pytest.raises(httpx.HTTPStatusError):
164166
_fetch_access_token(client, "test_device_code", 5)
167+
168+
169+
@pytest.mark.respx(base_url=settings.base_api_url)
170+
def test_notify_already_logged_in_user(
171+
respx_mock: respx.MockRouter, logged_in_cli: None
172+
) -> None:
173+
result = runner.invoke(app, ["login"])
174+
175+
assert result.exit_code == 0
176+
assert "You are already logged in." in result.output
177+
assert "Run fastapi logout first if you want to switch accounts." in result.output
178+
179+
180+
@pytest.mark.respx(base_url=settings.base_api_url)
181+
def test_notify_expired_token_user(
182+
respx_mock: respx.MockRouter, temp_auth_config: Path
183+
) -> None:
184+
past_exp = int(time.time()) - 3600
185+
expired_token = create_jwt_token({"sub": "test_user_12345", "exp": past_exp})
186+
187+
temp_auth_config.write_text(f'{{"access_token": "{expired_token}"}}')
188+
189+
with patch("fastapi_cloud_cli.commands.login.typer.launch") as mock_open:
190+
respx_mock.post(
191+
"/login/device/authorization", data={"client_id": settings.client_id}
192+
).mock(
193+
return_value=Response(
194+
200,
195+
json={
196+
"verification_uri_complete": "http://test.com",
197+
"verification_uri": "http://test.com",
198+
"user_code": "1234",
199+
"device_code": "5678",
200+
},
201+
)
202+
)
203+
respx_mock.post(
204+
"/login/device/token",
205+
data={
206+
"device_code": "5678",
207+
"client_id": settings.client_id,
208+
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
209+
},
210+
).mock(return_value=Response(200, json={"access_token": "new_token_1234"}))
211+
212+
result = runner.invoke(app, ["login"])
213+
214+
assert result.exit_code == 0
215+
assert "Your session has expired. Logging in again..." in result.output
216+
assert "Now you are logged in!" in result.output
217+
assert mock_open.called

0 commit comments

Comments
 (0)