Skip to content

Conversation

@premiumjibles
Copy link

@premiumjibles premiumjibles commented Dec 17, 2025

This adds graceful error handling to the affiliate revenue endpoint. Ensuring that if one swapper api fails we at least get partial data. It also will indicate in the API response which failed, ensuring we can show some kind of indicator in the UI.

Testing:

  • Make sure unchained ain't dead

If you want to properly test this locally:

  1. Install dependencies (from repo root)
    cd /home/sean/Repos/shapeshift-unchained
    yarn install

  2. Copy and configure env
    cp node/proxy/sample.env node/proxy/.env
    // Edit node/proxy/.env with real API keys (BEBOP_API_KEY, ZRX_API_KEY, etc.)

  3. Start reverse proxy (from repo root)
    docker-compose up -d reverse-proxy

  4. Start proxy API
    cd node/proxy
    docker-compose up

  5. Test endpoint (in another terminal)

curl "http://api.proxy.localhost/api/v1/affiliate/revenue?startTimestamp=$(( $(date +%s) - 604800 ))&endTimestamp=$(date +%s)"

Screenshots

image

Summary by CodeRabbit

  • New Features

    • Affiliate revenue responses now include details about which service providers failed to return fee data during retrieval.
  • Bug Fixes

    • Enhanced error handling enables the API to gracefully continue processing and return partial results when individual service providers encounter errors, preventing complete request failures and improving overall service resilience.

✏️ Tip: You can customize this high-level summary in your review settings.

@premiumjibles premiumjibles requested a review from a team as a code owner December 17, 2025 00:01
@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

📝 Walkthrough

Walkthrough

The affiliate revenue aggregator is enhanced to use Promise.allSettled instead of Promise.all, enabling graceful failure handling. A new failedProviders field tracks which providers failed to return data. Type definitions and OpenAPI schema are updated to reflect this new response structure.

Changes

Cohort / File(s) Summary
Affiliate Revenue Failure Tracking
node/proxy/api/src/affiliateRevenue/index.ts, node/proxy/api/src/models.ts, node/proxy/api/src/swagger.json
Replaced Promise.all with Promise.allSettled for provider fee fetches; added failedProviders: Service[] array to track failed providers; introduced providerNames mapping to correlate results; implemented result iteration with success/failure branching; updated AffiliateRevenueResponse interface to include failedProviders field; added Service enum schema to OpenAPI documentation with provider list.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify Promise.allSettled result handling logic correctly separates successful and failed providers
  • Cross-check type consistency between TypeScript interface and OpenAPI schema definitions
  • Confirm error logging and failedProviders population align across implementation and return contract

Possibly related PRs

  • shapeshift/unchained#1228: Initial implementation of affiliate revenue aggregator with parallel provider fetching; these changes extend that pattern with resilient error handling.

Poem

🐰 Hopping through the fees so fine,
Now failures don't make systems whine,
With allSettled paths so bright,
Each provider tracked just right!
Failed or not, they're logged with care,

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding graceful error handling to the affiliate revenue endpoint by switching from Promise.all to Promise.allSettled and tracking failed providers.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 11435_graceful_errors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
node/proxy/api/src/swagger.json (1)

131-145: Consider adding maxItems constraint to the array.

The failedProviders field is correctly defined and marked as required. The array is naturally bounded by the number of services (7), but adding an explicit maxItems constraint would make this clear in the API contract.

Based on the static analysis hint, you could apply this diff:

 					"failedProviders": {
 						"items": {
 							"$ref": "#/components/schemas/Service"
 						},
-						"type": "array"
+						"type": "array",
+						"maxItems": 7
 					},
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 026c8f1 and 579ccea.

📒 Files selected for processing (3)
  • node/proxy/api/src/affiliateRevenue/index.ts (4 hunks)
  • node/proxy/api/src/models.ts (1 hunks)
  • node/proxy/api/src/swagger.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
node/proxy/api/src/affiliateRevenue/index.ts (1)
node/proxy/api/src/models.ts (1)
  • Service (6-6)
🪛 Checkov (3.2.334)
node/proxy/api/src/swagger.json

[medium] 131-135: Ensure that arrays have a maximum number of items

(CKV_OPENAPI_21)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check
🔇 Additional comments (4)
node/proxy/api/src/models.ts (1)

10-10: LGTM!

The addition of failedProviders field correctly tracks which services failed, enabling the UI to display appropriate indicators.

node/proxy/api/src/affiliateRevenue/index.ts (2)

24-43: LGTM!

The switch to Promise.allSettled correctly implements graceful error handling. Failed providers are properly tracked and logged, while successful results are aggregated.


57-61: LGTM!

The return object correctly includes failedProviders, matching the updated AffiliateRevenueResponse interface.

node/proxy/api/src/swagger.json (1)

114-125: LGTM!

The Service enum schema correctly reflects all defined services and aligns with the TypeScript type definition.

import * as portals from './portals'
import { AffiliateRevenueResponse, Service, services } from '../models'

const providerNames: Service[] = ['zrx', 'bebop', 'thorchain', 'mayachain', 'chainflip', 'portals']
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Refactor to eliminate fragile order-dependent mapping.

The providerNames array is tightly coupled to the order of promises in Promise.allSettled (lines 26-33). If a developer adds or reorders providers, they must remember to update both locations identically, or failures will be attributed to the wrong provider.

Apply this diff to use a more maintainable structure:

-const providerNames: Service[] = ['zrx', 'bebop', 'thorchain', 'mayachain', 'chainflip', 'portals']
+const providers = [
+  { name: 'zrx' as Service, getFees: zrx.getFees },
+  { name: 'bebop' as Service, getFees: bebop.getFees },
+  { name: 'thorchain' as Service, getFees: thorchain.getFees },
+  { name: 'mayachain' as Service, getFees: mayachain.getFees },
+  { name: 'chainflip' as Service, getFees: chainflip.getFees },
+  { name: 'portals' as Service, getFees: portals.getFees },
+]

Then update the Promise.allSettled call:

-    const results = await Promise.allSettled([
-      zrx.getFees(startTimestamp, endTimestamp),
-      bebop.getFees(startTimestamp, endTimestamp),
-      thorchain.getFees(startTimestamp, endTimestamp),
-      mayachain.getFees(startTimestamp, endTimestamp),
-      chainflip.getFees(startTimestamp, endTimestamp),
-      portals.getFees(startTimestamp, endTimestamp),
-    ])
+    const results = await Promise.allSettled(
+      providers.map(p => p.getFees(startTimestamp, endTimestamp))
+    )
     
-    results.forEach((result, index) => {
+    results.forEach((result, index) => {
+      const provider = providers[index].name
       if (result.status === 'fulfilled') {
         fees.push(...result.value)
       } else {
-        const provider = providerNames[index]
         failedProviders.push(provider)
         console.error(`[AffiliateRevenue] ${provider} failed:`, result.reason)
       }
     })

Committable suggestion skipped: line range outside the PR's diff.


⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the affiliateRevenue directory structure
cd node/proxy/api/src/affiliateRevenue 2>/dev/null && find . -type f -name "*.ts" | head -20 || echo "Directory not found or no TypeScript files"

Repository: shapeshift/unchained

Length of output: 184


🏁 Script executed:

# Check for models.ts to see the Service enum
find . -name "models.ts" -path "*/affiliateRevenue/*" -exec cat {} \; 2>/dev/null || find . -name "models.ts" | grep -i affiliate | head -1 | xargs cat 2>/dev/null || echo "models.ts not found"

Repository: shapeshift/unchained

Length of output: 46


🏁 Script executed:

# Check for nearintents directory or files
fd -t f "nearintents" node/proxy/api/src/affiliateRevenue/ 2>/dev/null || echo "No nearintents files found in affiliateRevenue"

Repository: shapeshift/unchained

Length of output: 114


🏁 Script executed:

# Examine the index.ts file to see the actual providerNames and Promise.allSettled implementation
find . -path "*/affiliateRevenue/index.ts" -exec head -60 {} \; 2>/dev/null || echo "index.ts not found"

Repository: shapeshift/unchained

Length of output: 1980


🏁 Script executed:

find . -path "*/affiliateRevenue/../models.ts" -o -path "*/affiliateRevenue/models.ts" 2>/dev/null | xargs grep -A 20 "Service" | head -40

Repository: shapeshift/unchained

Length of output: 46


🏁 Script executed:

# Alternative search for models.ts
find . -name "models.ts" | xargs grep -B 2 -A 15 "type Service\|enum Service\|const services" 2>/dev/null | head -50

Repository: shapeshift/unchained

Length of output: 671


Add the missing nearintents provider to the affiliate revenue collection.

The nearintents service is defined in the Service enum but disconnected from the revenue collection flow. Import the nearIntents module, add it to the providerNames array, and include its getFees() call in the Promise.allSettled() array.

🤖 Prompt for AI Agents
In node/proxy/api/src/affiliateRevenue/index.ts around line 9, the providerNames
array is missing the 'nearintents' service and its module isn't imported or
invoked; import the nearIntents module at the top, add 'nearintents' to the
providerNames: Service[] array, and include a call to nearIntents.getFees() in
the Promise.allSettled() array so the nearintents provider participates in the
affiliate revenue collection flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants