Add live-verified Revolut, Swapter and NYM reporting plugins, and fix the broken test suite - #228
Open
j0ntz wants to merge 5 commits into
Open
Add live-verified Revolut, Swapter and NYM reporting plugins, and fix the broken test suite#228j0ntz wants to merge 5 commits into
j0ntz wants to merge 5 commits into
Conversation
j0ntz
force-pushed
the
jon/reports-server-umbrella
branch
from
July 30, 2026 20:59
b26a08c to
e522418
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e522418. Configure here.
The suite imported from ../lib/util, a gitignored build artifact that only exists after a build, so a clean checkout could not run any test. Point it at the source module instead. The analytics fixtures also still used the old pluginId key, which the current cleaner rejects.
The suite was not run by CI, which is how it stayed broken. The job installs with --ignore-scripts and generates the gitignored clientConfig.json before running mocha.
Queries Swapter's tool-history reporting endpoint and maps each order to a StandardTx. Pages are buffered so a mid-page failure retries idempotently, and progress only advances once the full walk completes.
Queries NYM's partner reporting endpoint and converts its native-unit amounts to major units using the live currencies list. The report timestamp keys off createdDate because completedDate is null even on settled orders. processNymTx follows the uniform (rawTx, pluginParams) processor contract, and an unparseable order fails its page rather than being skipped: skipping would let the walk complete and advance progress past an order the next run's lookback can no longer reach.
Queries Revolut Ramp's orders endpoint, authenticated with the X-API-KEY header that the GUI's existing ramp key uses. Two shapes of that API are easy to get wrong and are handled explicitly: the start/end bounds are date-only, and the response is a bare array paged by skip/limit rather than a cursor envelope. An order id is not unique in the response. Revolut returns one row per payment attempt, so the same id arrives both COMPLETED and FAILED. Since orderId keys the StandardTx document, attempts are collapsed to one winner per id, settled beating unsettled, before anything is emitted. Native assets resolve to their Edge chain with a null tokenId. Tokens resolve their chain but leave tokenId undefined: Revolut reports no contract address, and a guessed one would mis-price the asset.
j0ntz
force-pushed
the
jon/reports-server-umbrella
branch
from
July 31, 2026 21:19
e522418 to
74cfeee
Compare
2 tasks
3 tasks
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
noneDescription
The working half of the umbrella task
reports-server: Umbrella.
This PR carries only the providers whose keys are known to work, and every one of
them was driven against the live partner API before this was opened. The
providers that are still blocked on a credential or a partner API change are
split out to #231 so
they cannot hold this up.
Ordering matters: the test suite was broken on
master, so nothing else could betrusted until that was fixed. It goes first, then one commit per provider.
Commits
test/util.test.tsimported from../lib/util, a gitignored build artifact that only exists after a build, soa clean checkout could not run a single test. Repointed at
../src/demo/clientUtil, and renamed 4 stalepluginIdkeys topartnerIdin the analytics fixtures, which the current cleaner rejects.
suite stayed broken. Generates the gitignored
clientConfig.jsonthen runsmocha on every PR.
PR #219.
NYM partner reporting API.
below.
Revolut was not credential-blocked, it was pointed at the wrong API
The previous version of this plugin queried
api.revolut.com/partner/v1/transactionswith anAuthorization: Bearerheaderand got a 404, which had been read as an unprovisioned key. That endpoint does
not exist. The Revolut Ramp reporting API is:
and it answers with the key Edge already ships in the GUI
(
env.jsonRAMP_PLUGIN_INITS.revolut, which also carries that exact host asapiUrl). No new credential is needed. The plugin is rewritten accordingly, andthree things about that API are worth calling out because each one silently
breaks a naive port:
startandendare date-only (YYYY-MM-DD). An ISO date-time, epochseconds and epoch millis all return
400 Invalid field 'start'.skip/limitwalked until a short page, with no cursor and no total.
the same id arrives as both
COMPLETEDandFAILEDwith different amounts andupdated_at. SinceorderIdkeys theStandardTxdocument, attempts arecollapsed to one winner per id (settled beats unsettled, then latest
updated_at) before anything is emitted. On the live run this collapsed 1,359rows to 1,162 orders; without it, 197 orders would have been written twice with
an arbitrary attempt winning.
Native assets resolve to their Edge chain with
tokenId: null. Tokens resolvetheir chain but leave
tokenIdundefined on purpose: Revolut reports no contractaddress, and minting a tokenId from a guessed contract would mis-price the asset,
so the currency code carries the rates lookup instead.
nexchange was dropped as a duplicate
Evaluating
paul/partnerFixes(#229) showed it
already carries a nexchange plugin descended from the same origin as ours
(#217): the test files
are byte-identical and the plugins differ by 96 diff lines. #229 is the newer and
broader branch, by the port's original author, so shipping a second copy would
only conflict with it.
Two fixes from our port that #229 does not have are written up in
#231 and posted on
#229 itself, so neither is lost. nexchange's real blocker is a reporting-scoped
key and is independent of whose code lands.
Testing
Every provider here was driven end to end against the live partner API, read
only, using the GUI's existing
env.jsoncredentials. Production CouchDB wasnever touched.
Every emitted row was validated against the repo's own
asStandardTxcleaner:0 failures and 0 malformed rows across all three.
Static:
npm run build.typesclean,npm test64 passing 1 pending, eslintclean,
verify-repo.sh --base origin/masterpassed.Superseded PRs (close on merge)
Edge-owned, safe to close with a pointer here and delete the branches:
#225 (test suite + CI),
#227 (Revolut),
#224 (Swapter),
#226 (NYM).
Partner-owned fork PRs are lineage only, do not delete their branches:
#211 (Revolut),
#219 (Swapter).
Post-merge setup
Each plugin returns
[]silently when itsapiKeyis absent, so "no rows" afterdeploy looks identical to "not wired". Write the key into
reports_apps.partnerIds.<pluginId>.apiKeys.apiKeyin production CouchDB, restartreportsQuery, then confirm rows are arriving rather than assuming they are.revolutenv.jsonRAMP_PLUGIN_INITS.revolut.apiKeyswapterenv.jsonSWAPTER_INIT.apiKeynymswapenv.jsonNYM_SWAP_INIT.apiKeyAll three are human ops steps. No agent writes to production CouchDB.