🐛 Fix FASTAPI_CLOUD_TOKEN always overrides user token#180
Conversation
| @@ -557,7 +572,7 @@ def _send_waitlist_form( | |||
| ) -> None: | |||
| with toolkit.progress("Sending your request...") as progress: | |||
| with APIClient() as client: | |||
There was a problem hiding this comment.
Instantiate APIClient here instead of accepting it as a parameter because at the moment this function is called there is no APIClient instance yet in deploy. We can off course instantiate APIClient earlier in deploy, but it would increase indentation
| app_config = get_app_config(path_to_deploy) | ||
|
|
||
| if app_config and provided_app_id and app_config.app_id != provided_app_id: | ||
| if use_deploy: |
There was a problem hiding this comment.
The way GitHub shows diff of deploy command starting from this line is total mess..
Changes are not that big actually.
It's just:
- printing info message in case deployment token is used
- increasing the indentation caused by
with APIClient() - using
client.handle_http_errorsinstead ofhandle_http_errors - passing
clientparameter to some functions - passing arguments by names instead of positional arguments
It looks much better in VSCode
| POLL_MAX_RETRIES = 5 | ||
|
|
||
|
|
||
| def _handle_unauthorized(auth_mode: AuthMode) -> str: |
There was a problem hiding this comment.
_handle_unauthorized and handle_http_error were just moved from cli module with the following diff:
Details
-def handle_unauthorized() -> str:
+def _handle_unauthorized(auth_mode: AuthMode) -> str:
message = "The specified token is not valid. "
- identity = Identity()
-
- if identity.auth_mode == "user":
+ if auth_mode == "user":
delete_auth_config()
message += "Use `fastapi login` to generate a new token."
else:
message += "Make sure to use a valid token."
return message
-def handle_http_error(error: HTTPError, default_message: str | None = None) -> str:
+def handle_http_error(
+ error: httpx.HTTPError,
+ default_message: str | None = None,
+ auth_mode: AuthMode = "user",
+) -> str:
message: str | None = None
- if isinstance(error, HTTPStatusError):
+ if isinstance(error, httpx.HTTPStatusError):
status_code = error.response.status_code
# Handle validation errors from Pydantic models, this should make it easier to debug :)
if status_code == 422:
logger.debug(error.response.json()) # pragma: no cover
elif status_code == 401:
- message = handle_unauthorized()
+ message = _handle_unauthorized(auth_mode=auth_mode)
elif status_code == 403:
message = "You don't have permissions for this resource"
if not message:
message = (
default_message
or f"Something went wrong while contacting the FastAPI Cloud server. Please try again later. \n\n{error}"
)
return message| ) | ||
|
|
||
| @contextmanager | ||
| def handle_http_errors( |
There was a problem hiding this comment.
handle_http_errors was just moved from cli module with the following diff (omitted indentation for clearer diff):
Details
-@contextlib.contextmanager
+@contextmanager
def handle_http_errors(
+ self,
progress: Progress,
default_message: str | None = None,
) -> Generator[None, None, None]:
try:
yield
- except ReadTimeout as e:
+ except httpx.ReadTimeout as e:
logger.debug(e)
progress.set_error(
- "The request to the FastAPI Cloud server timed out. Please try again later."
+ "The request to the FastAPI Cloud server timed out."
+ " Please try again later."
)
raise typer.Exit(1) from None
- except HTTPError as e:
+ except httpx.HTTPError as e:
logger.debug(e)
- message = handle_http_error(e, default_message)
+ message = handle_http_error(e, default_message, auth_mode=self.auth_mode)
progress.set_error(message)
raise typer.Exit(1) from None| return _is_jwt_expired(self.token) | ||
| return _is_jwt_expired(self._user_token) | ||
|
|
||
| def is_logged_in(self) -> bool: |
There was a problem hiding this comment.
Previously, is_logged_in meant any of user token or deploy token exist. I think it was not very good naming (is_authenticated would be better).
Now is_logged_in is only about the user token (obtained as a result of login procedure).
Hope it will not be confusing (for me it's not).
| @pytest.fixture(autouse=True) | ||
| def unset_env_vars(monkeypatch: pytest.MonkeyPatch) -> Generator[None, None, None]: | ||
| """Fixture to unset environment variables that might interfere with tests.""" | ||
| monkeypatch.delenv("FASTAPI_CLOUD_TOKEN", raising=False) | ||
| yield |
There was a problem hiding this comment.
That was very annoying that tests stopped working when I had FASTAPI_CLOUD_TOKEN set on my machine.. Took me time to understand what is happening..
| def test_init_sentry_when_deployment_token( | ||
| logged_out_cli: Path, monkeypatch: pytest.MonkeyPatch | ||
| ) -> None: | ||
| monkeypatch.setenv("FASTAPI_CLOUD_TOKEN", "deployment-token") | ||
| with patch("fastapi_cloud_cli.utils.sentry.sentry_sdk.init") as mock_init: | ||
| init_sentry() | ||
|
|
||
| mock_init.assert_called_once_with( | ||
| dsn=SENTRY_DSN, | ||
| integrations=[ANY], # TyperIntegration instance | ||
| send_default_pii=False, | ||
| ) |
There was a problem hiding this comment.
This is not necessary for this PR, I just noticed that this use case wasn't covered by tests
buurro
left a comment
There was a problem hiding this comment.
Thank you for addressing this!
patrick91
left a comment
There was a problem hiding this comment.
Nice work! I've left a couple of nit comments 😊
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com>
This PR implements alternative approach to solve the same issue as #174
For now, if you have
FASTAPI_CLOUD_TOKENset and try to use any command, it will always use deploy token (see sources)This PR turns
Identityinto a just storage, and moves token selection logic toAPIClient. Now we can parameterizeAPIClientto use user token or deployment token depending on the context (command used).Now if user runs
fastapi loginandFASTAPI_CLOUD_TOKENis set, it will show a warning:fastapi loginAnd on
fastapi deploycommand, we also notify that deployment token is being used:fastapi deployAs a side effect I updated the output of
whoamicommand.Now it checks both user token and deployment token:
Details
Not logged in, but deployment token is set:
Logged in, and deployment token is set:
Without deployment token:
Additionally added a
unset_env_varsfixture that ensuresFASTAPI_CLOUD_TOKENset on host machine doesn't break tests (currently on master tests will fail if you haveFASTAPI_CLOUD_TOKENset)