-
Notifications
You must be signed in to change notification settings - Fork 433
refactor(utils): extract shared HTTP-post helper for delivery modules #952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
muddlebee
merged 5 commits into
Tracer-Cloud:main
from
mayankbharati-ops:refactor/864-shared-delivery-transport-helper
Apr 28, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
069c502
refactor(utils): extract shared HTTP-post helper for delivery modules
mayankbharati-ops b8b04c1
ci: retrigger after anyio/mcp circular-import flake
mayankbharati-ops 3f30698
fix(utils): address greptile review on delivery transport refactor
mayankbharati-ops 059958a
refactor(utils): address review nits — restore log key, rename field,…
mayankbharati-ops 9f08657
fix(slack): restore charset=utf-8 — Slack emits missing_charset warni…
mayankbharati-ops File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| """Shared HTTP-post transport for outbound message-delivery helpers. | ||
|
|
||
| Slack, Discord, and Telegram delivery modules each issue a JSON ``POST`` | ||
| to a provider endpoint, parse the response body, and return a | ||
| ``(success, error, ...)`` tuple. The transport pieces of that flow — | ||
| making the request, applying a timeout, catching network exceptions, and | ||
| attempting JSON decode — are identical; only the success criteria, | ||
| authentication scheme, and error-message extraction differ per provider. | ||
|
|
||
| This module hosts the shared transport so each delivery module can keep | ||
| its provider-specific payload building and result interpretation while | ||
| sharing one well-tested HTTP code path. | ||
|
|
||
| The helper deliberately does **not** decide whether the call succeeded | ||
| at the provider level. It returns ``ok=True`` whenever the request | ||
| completed without raising; callers then inspect ``status_code`` and | ||
| ``data``/``text`` to apply provider semantics (e.g. ``data["ok"]`` for | ||
| Slack, ``status_code in (200, 201)`` for Discord, ``status_code == 200`` | ||
| for Telegram). | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Mapping | ||
| from dataclasses import dataclass, field | ||
| from types import MappingProxyType | ||
| from typing import Any | ||
|
|
||
| import httpx | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class DeliveryResponse: | ||
| """Normalized result of a delivery POST. | ||
|
|
||
| Attributes: | ||
| ok: ``True`` iff the request completed without raising. This is a | ||
| transport-level signal only; provider-level success requires | ||
| inspecting ``status_code`` / ``data`` per provider semantics. | ||
| status_code: HTTP status code from the response, or ``0`` when the | ||
| request itself raised before a response was received. | ||
| data: Parsed JSON body when the response was a JSON object, | ||
| otherwise an empty mapping. Never ``None``, so callers can | ||
| chain ``.get(...)`` safely without a None-check. The mapping | ||
| is read-only (``MappingProxyType``) so the frozen dataclass | ||
| stays fully immutable end-to-end. | ||
| text: Raw response body, useful for fallback error extraction | ||
| when the body is not valid JSON or is empty. | ||
| error: String form of the exception that aborted the request. | ||
| Empty when ``ok`` is True. | ||
| exc_type: Class name of the exception that aborted the request | ||
| (e.g. ``"TimeoutError"``, ``"ConnectError"``). Empty when | ||
| ``ok`` is True. Surfaced separately so callers can include | ||
| the exception shape in triage logs without parsing ``error``. | ||
| Named ``exc_type`` rather than ``type`` because ``type`` is | ||
| a Python builtin. | ||
| """ | ||
|
|
||
| ok: bool | ||
| status_code: int = 0 | ||
| data: Mapping[str, Any] = field(default_factory=dict) | ||
| text: str = "" | ||
| error: str = "" | ||
| exc_type: str = "" | ||
|
|
||
| def __post_init__(self) -> None: | ||
| # Wrap ``data`` in a read-only view so callers cannot mutate the | ||
| # response after the fact and break the frozen-dataclass contract. | ||
| # ``object.__setattr__`` is required because ``frozen=True`` blocks | ||
| # normal attribute assignment. | ||
| if not isinstance(self.data, MappingProxyType): | ||
| object.__setattr__(self, "data", MappingProxyType(dict(self.data))) | ||
|
|
||
|
|
||
| def post_json( | ||
| url: str, | ||
| payload: dict[str, Any], | ||
| *, | ||
| headers: dict[str, str] | None = None, | ||
| timeout: float = 15.0, | ||
| follow_redirects: bool = False, | ||
| ) -> DeliveryResponse: | ||
| """POST ``payload`` as JSON to ``url`` and return a normalized result. | ||
|
|
||
| On request exceptions the result carries ``ok=False`` and ``error`` | ||
| set to the exception message — callers are not expected to handle | ||
| raised errors. The transport never re-raises. | ||
|
|
||
| Args: | ||
| url: Absolute URL to post to. | ||
| payload: JSON-serializable dict body. | ||
| headers: Optional headers (e.g. ``Authorization``). Defaults to | ||
| an empty dict; httpx will still set ``Content-Type`` and the | ||
| standard headers it manages. | ||
| timeout: Request timeout in seconds. Defaults to 15s, matching | ||
| the pre-existing per-provider timeouts. | ||
| follow_redirects: Whether to follow 3xx redirects. Disabled by | ||
| default to match Slack/Discord/Telegram REST APIs (which never | ||
| redirect on success). Slack incoming webhooks and the NextJS | ||
| ``/api/slack`` proxy enable it. | ||
|
|
||
| Returns: | ||
| ``DeliveryResponse`` with ``ok``, ``status_code``, ``data``, | ||
| ``text``, and ``error`` populated. JSON decode failures are | ||
| non-fatal: ``data`` falls back to ``{}`` and ``text`` always | ||
| carries the raw body. | ||
| """ | ||
| try: | ||
| response = httpx.post( | ||
| url, | ||
| json=payload, | ||
| headers=headers or {}, | ||
| timeout=timeout, | ||
| follow_redirects=follow_redirects, | ||
| ) | ||
| except Exception as exc: # noqa: BLE001 — transport never re-raises | ||
| return DeliveryResponse(ok=False, error=str(exc), exc_type=type(exc).__name__) | ||
|
|
||
| text = response.text | ||
| data: dict[str, Any] = {} | ||
| try: | ||
| parsed = response.json() | ||
| if isinstance(parsed, dict): | ||
| data = parsed | ||
| except Exception: # noqa: BLE001 — non-JSON body is permitted | ||
| pass | ||
|
|
||
| return DeliveryResponse( | ||
| ok=True, | ||
| status_code=response.status_code, | ||
| data=data, | ||
| text=text, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.