-
refactor(utils): extract shared HTTP-post helper for delivery modules (#952)
发布于
2026-04-28 01:16:23 +00:00 - refactor(utils): extract shared HTTP-post helper for delivery modules
Closes #864.
app/utils/slack_delivery.py,app/utils/discord_delivery.py, and
app/utils/telegram_delivery.pyeach issuedhttpx.postdirectly,
applied a per-provider timeout, parsed the response body, and converted
exceptions to(False, error, ...)tuples. The transport pieces were
identical; only the success criteria, auth scheme, and error-message
extraction differed per provider.Add
app/utils/delivery_transport.pywithpost_jsonplus a
DeliveryResponsedataclass that captures the shared transport
behavior: HTTP POST, timeout, optionalfollow_redirects, exception
suppression, and JSON decoding with graceful fallback. The helper
deliberately does not decide provider-level success — callers inspect
status_code/dataper their own semantics.Refactored callers:
- Slack:
_call_reactions_api,_post_direct,_post_via_webapp,
_post_via_incoming_webhook— all four code paths now go through
post_json.send_slack_reportorchestration unchanged. - Discord:
post_discord_message,create_discord_thread— same
Bearer/Bot header pattern factored into_discord_auth_headers. - Telegram:
post_telegram_message— bot-token redaction in error
messages preserved by re-running_redact_tokenover
response.error.
Provider-specific payload building, success criteria
(data["ok"]for Slack, status codes for Discord/Telegram), and
error extraction all stay in the calling modules. Public function
signatures are unchanged.Tests:
tests/utils/test_delivery_transport.py(new) — 15 tests covering
happy path, every transport-failure shape (httpx.ConnectError,
ReadTimeout, RequestError, OSError, RuntimeError), JSON-decode
fallbacks (non-JSON body, JSON array, empty body), header / timeout /
follow_redirects pass-through, and DeliveryResponse frozen-dataclass
invariants.tests/utils/test_slack_delivery.py(new) — 24 tests covering all
four Slack code paths,send_slack_reportorchestration, and
fallback chaindirect → webapp.tests/utils/test_discord_delivery.py(extended) — 3 new tests
pinning the helper-delegation contract: module no longer imports
httpx,post_discord_messageandcreate_discord_thread
both go throughpost_json.tests/utils/test_telegram_delivery.py(extended) — 3 new tests
pinning the same contract plus a regression test that the bot-token
is redacted out of error strings even when the failure originates
from the shared transport.- All pre-existing tests in
test_discord_delivery.pyand
test_telegram_delivery.pyupdated to patch
app.utils.delivery_transport.httpx.post(the new transport
boundary) instead of the per-modulehttpx.postimport that no
longer exists.
Verification:
pytest tests/: 3191 pass, 2 skipped, 1 xfailed, 0 failures.pytest tests/utils/: 105 pass, 1 skipped (was 32 + 26 = 58 across
delivery modules; now 105 with helper + Slack additions).ruff check app/ tests/andruff format --check: clean.mypy app/utils/: clean on touched modules; the 5 reported errors
are pre-existing missing-stub warnings in
app/integrations/{mariadb,mysql,hf_remote}unrelated to this PR.
- ci: retrigger after anyio/mcp circular-import flake
The previous CI run hit a known transient ImportError in
anyio.lowlevel/mcp when opensre integrations list ran on a
pytest-xdist worker. Reproduces zero times locally on origin/main and
on this branch; pushing an empty commit to re-run with a fresh
interpreter pool.- fix(utils): address greptile review on delivery transport refactor
- Wrap
DeliveryResponse.datainMappingProxyTypeso the frozen
dataclass is fully immutable end-to-end. Mutating the parsed body
now raisesTypeErrorinstead of silently succeeding, and caller-
passed dicts can no longer leak mutations into the response. - Add
error_typefield onDeliveryResponsepopulated with
type(exc).__name__on transport failures.slack_delivery._post_direct
threads it back into the exception log so on-call can distinguish
TimeoutErrorfromConnectionErrorat a glance — restores the
pre-refactor log shape that #864 had dropped. - Add
TestDelegatesToSharedTransporttotests/utils/test_slack_delivery.py,
mirroring the regression class on the discord/telegram test files.
Pins thatslack_deliverydoes not importhttpxand that all four
code paths (_call_reactions_api,_post_direct,_post_via_webapp,
_post_via_incoming_webhook) route throughdelivery_transport.post_json. - Add focused tests for the immutable-data, error_type, and slack
exc_type-log behaviours (+17 tests; 3208 pass / 2 skipped / 1 xfail).
Tighten
_discord_error_from_datasignature toMapping[str, Any]so
mypy stays clean against the new read-onlydatatype.- refactor(utils): address review nits — restore log key, rename field, drop redundant header, fix CodeQL
- slack_delivery
_post_directexception log key reverted totype=%s
(matching the exact pre-refactor formattype=%s channel=%s thread_ts=%s detail=%s) so existing log parsers that key off
type=keep working. Per @muddlebee's nit on PR #952. - DeliveryResponse field renamed
error_type→exc_type. Pythonic
abbreviation for "exception type"; can't betypebecause that
shadows the builtin. Field/log-key are intentionally distinct: the
field is the Python attribute, the log key matches the legacy format. - Drop
Content-Type: application/json; charset=utf-8from
_slack_bearer_headersand_discord_auth_headers.httpx.post
already setsContent-Type: application/jsonautomatically when the
request uses thejson=kwarg, so the explicit header was
redundant. No behavioural change for Slack/Discord (UTF-8 is httpx's
default encoding and neither provider parses the charset suffix). - Fix CodeQL py/import-and-import-from alerts on tests #503/#504. The
test_module_does_not_import_httpxregression test in each
delivery test file was importing the module under test via both
import X as modandfrom X import Ystyles. Switched to a
singlefrom app.utils import <module>style so the same module
is no longer dual-imported.
Test count unchanged (3208 pass / 2 skipped / 1 xfail). Lint, format,
andmypy app/utils/clean.- fix(slack): restore charset=utf-8 — Slack emits missing_charset warning without it
Live e2e probe against real
slack.com/api/chat.postMessagerevealed
that dropping the explicitContent-Type: application/json; charset=utf-8
header (per the previous nit-fix commit) caused Slack to add a
missing_charsetentry toresponse_metadata.warningson every
request. httpx alone sets only the bareapplication/jsonforjson=
kwargs, but Slack's docs explicitly recommend the charset suffix
(https://api.slack.com/web#posting_json) and emit the warning otherwise.Restored the
charset=utf-8header on_slack_bearer_headerswith
an inline comment explaining the reason._discord_auth_headersstays
auth-only (Discord does not emit this warning, and its docs do not
require the charset suffix).Pinned via direct header assertions on the two existing slack tests that
already captured headers (test_sends_correct_url_and_headersfor the
reactions API,test_sends_thread_reply_payloadfor chat.postMessage),
so a future regression that re-drops the charset will fail loudly.Verified live:
- direct
chat.postMessageprobe now returnswarnings=[](was
['missing_charset']before this commit). - 38/38 live checks against real Slack / Discord / Telegram pass.
- 3208 unit tests pass; lint / format / mypy clean.
下载附件