This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow-steward.git


The following commit(s) were added to refs/heads/main by this push:
     new 76dcb97  fix(gmail-oauth-draft): wrap api_get with HTTPError handler 
for clean failure surface (#240)
76dcb97 is described below

commit 76dcb97778a9597c70b63a3562fd41e5c287d1aa
Author: AndrĂ© Ahlert <[email protected]>
AuthorDate: Wed May 20 05:17:43 2026 -0300

    fix(gmail-oauth-draft): wrap api_get with HTTPError handler for clean 
failure surface (#240)
    
    `api_get` was the only `urllib.request.urlopen` call in the package
    without a `try/except urllib.error.HTTPError`. `api_post`,
    `mark_threads_read` (both calls), and the token refresh in
    `credentials.py` all wrap the call and raise `SystemExit` with a
    one-line `"Gmail API <path> failed (<code>): <body>"` message.
    
    `api_get` is the path that `latest_reply_headers` uses, so any
    401 / 403 / 404 from `GET /threads/{id}?format=full` surfaced as a
    raw Python traceback instead of the clean error the rest of the
    tool produces. That path is exercised on every
    `oauth-draft-create --thread-id <X>` invocation that does not pass
    `--no-reply-headers`.
    
    Wraps `api_get` with the same handler as `api_post` and adds a
    regression test mirroring `test_api_post_raises_on_http_error`.
---
 .../oauth-draft/src/oauth_draft/create_draft.py    |  7 +++++--
 tools/gmail/oauth-draft/tests/test_create_draft.py | 23 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/tools/gmail/oauth-draft/src/oauth_draft/create_draft.py 
b/tools/gmail/oauth-draft/src/oauth_draft/create_draft.py
index e24fcc6..bfc07e9 100644
--- a/tools/gmail/oauth-draft/src/oauth_draft/create_draft.py
+++ b/tools/gmail/oauth-draft/src/oauth_draft/create_draft.py
@@ -61,8 +61,11 @@ def api_get(access_token: str, path: str) -> dict:
         f"{GMAIL_API}{path}",
         headers={"Authorization": f"Bearer {access_token}"},
     )
-    with urllib.request.urlopen(req, timeout=15) as r:
-        return json.loads(r.read())
+    try:
+        with urllib.request.urlopen(req, timeout=15) as r:
+            return json.loads(r.read())
+    except urllib.error.HTTPError as e:
+        raise SystemExit(f"Gmail API {path} failed ({e.code}): 
{e.read().decode(errors='replace')}") from e
 
 
 def api_post(access_token: str, path: str, payload: dict) -> dict:
diff --git a/tools/gmail/oauth-draft/tests/test_create_draft.py 
b/tools/gmail/oauth-draft/tests/test_create_draft.py
index d643962..a50056e 100644
--- a/tools/gmail/oauth-draft/tests/test_create_draft.py
+++ b/tools/gmail/oauth-draft/tests/test_create_draft.py
@@ -250,6 +250,29 @@ def test_api_post_parses_json_response():
     assert json.loads(request.data) == {"message": {"raw": "X"}}
 
 
+def test_api_get_raises_on_http_error():
+    """``api_get`` must surface HTTP errors as a clean ``SystemExit``.
+
+    Regression: ``api_get`` was the only ``urlopen`` call in the
+    package without a ``try/except HTTPError``, so a 401/403/404 from
+    ``threads.get`` (the path exercised on every ``oauth-draft-create
+    --thread-id <X>`` invocation) produced a raw Python traceback
+    instead of a one-line error matching the rest of the tool.
+    """
+    err = urllib.error.HTTPError(
+        url="https://x";,
+        code=404,
+        msg="Not Found",
+        hdrs=None,  # type: ignore[arg-type]
+        fp=io.BytesIO(b'{"error": "thread missing"}'),
+    )
+    with patch("oauth_draft.create_draft.urllib.request.urlopen", 
side_effect=err):
+        with pytest.raises(SystemExit) as excinfo:
+            api_get("token", "/threads/missing-thread")
+    assert "failed (404)" in str(excinfo.value)
+    assert "thread missing" in str(excinfo.value)
+
+
 def test_api_post_raises_on_http_error():
     err = urllib.error.HTTPError(
         url="https://x";,

Reply via email to