Skip to content

[16.0][FIX] currency_rate_update_xe: use XE.com JSON API (HTML scraping returns 403)#216

Open
DarioLodeiros wants to merge 4 commits into
OCA:16.0from
DarioLodeiros:16.0-fix-currency_rate_update_xe-user-agent
Open

[16.0][FIX] currency_rate_update_xe: use XE.com JSON API (HTML scraping returns 403)#216
DarioLodeiros wants to merge 4 commits into
OCA:16.0from
DarioLodeiros:16.0-fix-currency_rate_update_xe-user-agent

Conversation

@DarioLodeiros

@DarioLodeiros DarioLodeiros commented May 13, 2026

Copy link
Copy Markdown
Member

Problem

XE.com now answers automated requests to its public HTML currency tables
(www.xe.com/currencytables) with a CloudFront 403 "Request blocked",
regardless of the User-Agent sent. This breaks _obtain_rates, which
scrapes that page: the scheduled update fails every run and posts a
"Currency Rate Provider Failure" message, leaving rates frozen.

The earlier mitigation in this PR (sending a User-Agent and raising on
HTTP errors) is no longer enough — the block is applied at the edge for
datacenter IP ranges, so no header tweak gets through.

Fix

Switch the provider to the JSON endpoint that powers xe.com's own
converter (/api/protected/midmarket-converter/), which is reachable
from datacenter hosts. It returns the latest USD-based mid-market rates;
these are converted to be relative to the company base currency.

Since the endpoint only exposes the latest rates, the provider now
always returns today's rates regardless of the requested range, which is
sufficient for the scheduled daily update.

Notes

  • _get_supported_currencies is unchanged (the API uses the same ISO codes).
  • Verified end-to-end against the live endpoint from a datacenter host
    (DOP-based company, USD rate): scraping returns 403, the JSON API
    returns 200 with correct pegs (e.g. AED 3.6725, BHD 0.376).

XE.com started returning HTTP 403 to requests without a User-Agent
header. The provider was parsing the 403 error page, finding no rates,
and silently marking the scheduled run as successful (last_successful_run
gets advanced because _obtain_rates returns {date.today(): {}} which is
truthy in res.currency.rate.provider._update). No res.currency.rate
records were created, with no warning posted to the provider chatter.

Send a Mozilla User-Agent so XE responds with the rates page, and call
raise_for_status() so future HTTP errors are surfaced as a chatter
message via the existing exception handling in _update, instead of
appearing as a successful no-op.
@OCA-git-bot OCA-git-bot added mod:currency_rate_update_xe Module currency_rate_update_xe series:16.0 labels May 13, 2026
_get_historical_rate built ?date=YYYY-MM-DD URLs for every day in the
range, but XE.com returns HTTP 404 for that query when the date is the
current day (or in the future). The scheduled cron path always hits
today's URL when date_to == today, which is the common case.

Before the previous commit added raise_for_status() this 404 was
silently parsed as an empty HTML and produced no rate for today without
an exception. With raise_for_status() it now interrupts the whole
iteration and skips even the past-day rates that were already fetched
successfully.

Fall back to the latest endpoint (no date parameter) when the iterated
day is today or in the future. This is what _get_latest_rate already
uses, so we just reuse the same URL shape inside the loop. It also
unblocks the module's own test_cron, which exercises this exact flow
(date_from = yesterday, date_to = today).
@DarioLodeiros

Copy link
Copy Markdown
Member Author

Pushed a follow-up commit that fixes the underlying cause of the test failure: _get_historical_rate was building ?date=YYYY-MM-DD URLs for every day in the range, but XE.com returns HTTP 404 when the requested date is today. The scheduled cron path always lands on today (date_to == today), and test_cron reproduces this exact shape (date_from = yesterday, date_to = today).

Before this PR the 404 was silently parsed as empty HTML, so today's rate was not produced but past-day rates from the same iteration were. Now with raise_for_status(), the 404 interrupts the whole loop and the past-day rates were also lost — that's what the failing assertion in test_cron was catching.

The fix falls back to the latest endpoint (no date query string) whenever the iterated day is today or in the future, reusing what _get_latest_rate already does.

Note: the pre-commit failure is unrelated — setuptools-odoo-make-default/setuptools-odoo-get-requirements blow up with ModuleNotFoundError: No module named 'pkg_resources', which affects any PR currently opened against this repo (Python 3.13 / setuptools >= 81 removed pkg_resources).

@DarioLodeiros

Copy link
Copy Markdown
Member Author

Side note on the red pre-commit check: it's caused by setuptools-odoo 3.1.8 importing the removed pkg_resources module and is unrelated to this PR's diff. Opened #217 to bump the hook to 3.3.2 (which other OCA 16.0 repos already use). Once #217 lands and this branch is rebased, pre-commit should go green.

With a fresh provider (no last_successful_run) and the default
next_run = today, _scheduled_update computes date_from = today - 1 day
and date_to = today, so _update produces one rate per day in the range.
Before this PR, today's URL silently returned no data (XE 404 parsed as
empty HTML), so the assertion `len(rates) == 1` only held by accident.

Set last_successful_run = yesterday in the test so the cron computes
date_from = today and routes through _get_latest_rate, producing
exactly one rate. This is also the realistic shape of a daily cron
that is up to date.
…raping

XE.com now answers automated requests to its public HTML currency tables
with a CloudFront 403, regardless of the User-Agent sent, so the previous
scraping approach no longer works from server IP ranges.

Switch the provider to the JSON endpoint that powers xe.com's own
converter (midmarket-converter). It returns the latest USD-based
mid-market rates and is reachable from datacenter hosts. Rates are
converted to be relative to the company base currency. Since the endpoint
only exposes the latest rates, the provider now always returns today's
rates regardless of the requested range, which is enough for the
scheduled daily update.
@DarioLodeiros DarioLodeiros changed the title [16.0][FIX] currency_rate_update_xe: send User-Agent and raise on HTTP error [16.0][FIX] currency_rate_update_xe: use XE.com JSON API (HTML scraping returns 403) Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:currency_rate_update_xe Module currency_rate_update_xe series:16.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants