Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Aug 1, 2025

Summary by CodeRabbit

  • New Features

    • Added structured logging and retrieval of import error logs, including a downloadable CSV of import errors per import.
    • Introduced normalization of imported commission and sale amounts to USD using real-time FX rates for all import sources.
    • Enhanced import error handling with detailed, structured error logs for failed partner, customer, link, and commission imports.
    • Email notifications after imports now include a direct link to download import error logs.
  • Bug Fixes

    • Improved currency handling by ensuring all currency codes are consistently lowercased and normalized.
  • Refactor

    • Updated import payloads and schemas to require and propagate a unique import ID for traceability.
    • Streamlined code for currency conversion and error logging across all importers.
  • Chores

    • Added new data sources and pipes for logging and retrieving import errors with retention policies.

@vercel
Copy link
Contributor

vercel bot commented Aug 1, 2025

The latest updates on your projects. Learn more about Vercel for Git β†—οΈŽ

Name Status Preview Updated (UTC)
dub βœ… Ready (Inspect) Visit Preview Aug 4, 2025 0:50am

@devkiran devkiran marked this pull request as draft August 1, 2025 12:50
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 1, 2025

Warning

Rate limit exceeded

@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 44 seconds before requesting another review.

βŒ› How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between ed41840 and 178bdae.

πŸ“’ Files selected for processing (1)
  • apps/web/lib/analytics/convert-currency.ts (1 hunks)

Walkthrough

This change introduces a comprehensive import error logging system for external partner data imports. It adds structured error log schemas, ingestion endpoints, and retrieval pipes, enabling error logs to be recorded and downloaded as CSVs. Currency normalization to USD is implemented for imported commissions, and import IDs are now generated and propagated throughout import actions, schemas, and email notifications.

Changes

Cohort / File(s) Change Summary
API Route for Error Log Download
apps/web/app/api/workspaces/[idOrSlug]/import/[importId]/download/route.ts
Adds a GET API route to download import error logs as a CSV file for a workspace and import ID.
Import ID Propagation in Actions
apps/web/lib/actions/partners/create-program.ts,
apps/web/lib/actions/partners/start-partnerstack-import.ts,
apps/web/lib/actions/partners/start-rewardful-import.ts,
apps/web/lib/actions/partners/start-tolt-import.ts
Generates and propagates a unique importId when queuing partner import jobs for Rewardful, Tolt, and PartnerStack.
Currency Conversion Enhancements
apps/web/lib/analytics/convert-currency.ts
Refactors currency conversion logic and introduces convertCurrencyWithFxRates for explicit FX rate usage.
ID Generation Utility Update
apps/web/lib/api/create-id.ts
Adds "import_" to allowed prefixes and updates createId to accept optional parameters.
PartnerStack Import: FX and Currency Logging
apps/web/lib/partnerstack/import-commissions.ts
Adds FX rate fetching and USD normalization for commission imports; updates function signatures for FX rates.
PartnerStack Schema Updates
apps/web/lib/partnerstack/schemas.ts
Adds importId to import payload schema and currency to commission schema.
Rewardful API Cleanup
apps/web/lib/rewardful/api.ts
Removes debug logging.
Rewardful Import: FX, Logging, and Error Handling
apps/web/lib/rewardful/import-commissions.ts,
apps/web/lib/rewardful/import-customers.ts,
apps/web/lib/rewardful/import-partners.ts
Adds importId propagation, FX conversion to USD, structured error logging, and updates error handling and logging.
Rewardful Schema Update
apps/web/lib/rewardful/schemas.ts
Adds required importId field to the import payload schema.
Tolt API Cleanup
apps/web/lib/tolt/api.ts
Simplifies program parsing logic.
Tolt Import: FX and Currency Handling
apps/web/lib/tolt/import-commissions.ts
Adds FX conversion to USD and propagates program currency and FX rates in commission import.
Tolt Schema Updates
apps/web/lib/tolt/schemas.ts
Adds importId to import payload and currency_code to program schema.
Sales Schema Currency Normalization
apps/web/lib/zod/schemas/sales.ts
Ensures currency is always lowercased in sale event schema.
Tinybird: Error Log Ingestion and Retrieval
apps/web/lib/tinybird/get-import-error-logs.ts,
apps/web/lib/tinybird/log-import-error.ts
Adds modules for ingesting and retrieving import error logs using Tinybird.
Import Error Log Schema
apps/web/lib/zod/schemas/import-error-log.ts
Introduces a Zod schema for structured import error logs.
Tinybird Datasource for Error Logs
packages/tinybird/datasources/dub_import_error_logs.datasource
Adds a new datasource for structured import error logs with TTL and partitioning.
Tinybird Pipe for Error Log Retrieval
packages/tinybird/pipes/get_import_error_logs.pipe
Adds a pipe to fetch import error logs filtered by workspace and import IDs.
PartnerStack Email Template Update
apps/web/lib/partnerstack/update-stripe-customers.ts
Switches notification email template to ProgramImported.
Email Template Enhancement
packages/email/src/templates/program-imported.tsx
Renames component, adds optional importId, and conditionally renders a download link for import logs.
Bulk Delete Links Script Update
apps/web/scripts/bulk-delete-links.ts
Refactors link deletion logic to update Tinybird before DB deletion and adjusts filtering criteria and logging.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant API (GET /download)
    participant withWorkspace
    participant getImportErrorLogs
    participant CSV Formatter

    User->>API (GET /download): Requests error logs for workspace/importId
    API (GET /download)->>withWorkspace: Validates workspace context
    withWorkspace->>getImportErrorLogs: Fetches error logs by workspaceId/importId
    getImportErrorLogs-->>withWorkspace: Returns error log data
    withWorkspace->>CSV Formatter: Maps logs to CSV format
    CSV Formatter-->>API (GET /download): Returns CSV data
    API (GET /download)-->>User: Sends CSV file as response
Loading
sequenceDiagram
    participant ImportAction
    participant createId
    participant ImporterQueue
    participant ImportWorker
    participant logImportError

    ImportAction->>createId: Generate importId (prefix "import_")
    ImportAction->>ImporterQueue: Queue import job with importId
    ImporterQueue->>ImportWorker: Processes import job
    ImportWorker->>logImportError: Logs structured errors with importId
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • dubinc/dub#2642: Implements the PartnerStack importer integration, including API routes and import logic. Related due to shared focus on partner import processes and error log handling.

Poem

A bunny hopped through fields of code,
Logging errors on each import road.
With FX rates and IDs anew,
It normalized currencies too!
Now, partners and logs in harmony dwell,
And CSVs download with a single shell.
πŸ‡βœ¨

✨ Finishing Touches
  • πŸ“ Generate Docstrings
πŸ§ͺ Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch importer-handle-currency

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
πŸͺ§ Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@devkiran devkiran changed the title Update import-commissions.ts Ensure that amounts are accurately converted to USD Aug 1, 2025
@devkiran devkiran changed the title Ensure that amounts are accurately converted to USD Ensure amounts are accurately converted to USD Aug 1, 2025
@devkiran devkiran marked this pull request as ready for review August 3, 2025 10:01
@devkiran devkiran requested a review from steven-tey August 3, 2025 10:01
Copy link
Contributor

@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: 0

πŸ”­ Outside diff range comments (1)
apps/web/lib/partnerstack/import-commissions.ts (1)

180-188: Potential inconsistency in duplicate commission detection.

The duplicate commission check on Line 186 still uses the original commission.transaction.amount instead of the converted amount. This could cause issues when comparing commissions that have been currency-converted versus those that haven't.

       amount: commission.transaction.amount,
+      amount: amount,
🧹 Nitpick comments (5)
apps/web/lib/zod/schemas/import-logs.ts (1)

3-20: Well-structured import log schema with comprehensive validation.

The schema provides good type safety with appropriate enum constraints for source platforms, entity types, and error codes. The field structure supports effective import tracking and debugging.

Consider adding success codes to the code enum (e.g., "SUCCESS", "CREATED", "UPDATED") to provide complete import status tracking alongside error cases.

apps/web/lib/tinybird/record-import-logs.ts (1)

10-20: Consider removing or conditionalizing the console log.

The implementation is solid with proper validation and early return for empty arrays. However, the console log on line 17 might be noisy in production environments.

Consider using a conditional log or removing it:

- console.log("importLogs", logs.length);
+ if (process.env.NODE_ENV === "development") {
+   console.log("importLogs", logs.length);
+ }
packages/email/src/templates/campaign-imported.tsx (1)

62-76: Consider using a configurable API base URL.

The conditional rendering and security attributes are implemented correctly. However, the hardcoded API URL could cause issues in different environments.

Consider using an environment variable or constant for the API base URL:

- href={`https://api.dub.co/${workspace.slug}/import/${importId}/download`}
+ href={`${process.env.NEXT_PUBLIC_API_URL || 'https://api.dub.co'}/${workspace.slug}/import/${importId}/download`}
apps/web/app/api/workspaces/[idOrSlug]/import/[importId]/download/route.ts (1)

6-29: Consider adding error handling and input validation.

The implementation correctly fetches and converts import logs to CSV format with appropriate headers. However, consider adding explicit error handling for the async operations and input validation for the importId parameter.

 export const GET = withWorkspace(async ({ workspace, params }) => {
   const { importId } = params;
+
+  if (!importId || typeof importId !== 'string') {
+    return new Response('Invalid import ID', { status: 400 });
+  }

-  const importLogs = await getImportLogs({
-    workspaceId: workspace.id,
-    importId,
-  });
+  try {
+    const importLogs = await getImportLogs({
+      workspaceId: workspace.id,
+      importId,
+    });
+  } catch (error) {
+    console.error('Failed to fetch import logs:', error);
+    return new Response('Failed to fetch import logs', { status: 500 });
+  }
apps/web/lib/tolt/import-commissions.ts (1)

185-206: Consider consistent currency case handling.

The currency conversion logic is correct, but there's a minor inconsistency: the currency parameter is passed as lowercase from importCommissions (line 69) but is then converted to uppercase for comparison. Consider standardizing to either uppercase or lowercase throughout for consistency.

-  if (currency.toUpperCase() !== "USD" && fxRates) {
+  if (currency !== "usd" && fxRates) {

and

-  if (currency.toUpperCase() !== "USD" && fxRates) {
+  if (currency !== "usd" && fxRates) {
πŸ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 7798a39 and 6e517ff.

πŸ“’ Files selected for processing (24)
  • apps/web/app/api/workspaces/[idOrSlug]/import/[importId]/download/route.ts (1 hunks)
  • apps/web/lib/actions/partners/create-program.ts (1 hunks)
  • apps/web/lib/actions/partners/start-partnerstack-import.ts (2 hunks)
  • apps/web/lib/actions/partners/start-rewardful-import.ts (2 hunks)
  • apps/web/lib/actions/partners/start-tolt-import.ts (2 hunks)
  • apps/web/lib/analytics/convert-currency.ts (1 hunks)
  • apps/web/lib/api/create-id.ts (1 hunks)
  • apps/web/lib/partnerstack/import-commissions.ts (9 hunks)
  • apps/web/lib/partnerstack/schemas.ts (2 hunks)
  • apps/web/lib/rewardful/api.ts (0 hunks)
  • apps/web/lib/rewardful/import-commissions.ts (13 hunks)
  • apps/web/lib/rewardful/import-customers.ts (7 hunks)
  • apps/web/lib/rewardful/import-partners.ts (4 hunks)
  • apps/web/lib/rewardful/schemas.ts (1 hunks)
  • apps/web/lib/tinybird/get-import-logs.ts (1 hunks)
  • apps/web/lib/tinybird/record-import-logs.ts (1 hunks)
  • apps/web/lib/tolt/api.ts (1 hunks)
  • apps/web/lib/tolt/import-commissions.ts (10 hunks)
  • apps/web/lib/tolt/schemas.ts (2 hunks)
  • apps/web/lib/types.ts (2 hunks)
  • apps/web/lib/zod/schemas/import-logs.ts (1 hunks)
  • packages/email/src/templates/campaign-imported.tsx (3 hunks)
  • packages/tinybird/datasources/dub_import_logs.datasource (1 hunks)
  • packages/tinybird/pipes/get_import_logs.pipe (1 hunks)
πŸ’€ Files with no reviewable changes (1)
  • apps/web/lib/rewardful/api.ts
🧰 Additional context used
🧠 Learnings (6)
πŸ“š Learning: in apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition ob...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.

Applied to files:

  • apps/web/lib/rewardful/schemas.ts
  • apps/web/lib/types.ts
  • apps/web/lib/tolt/schemas.ts
  • apps/web/lib/partnerstack/schemas.ts
  • apps/web/lib/zod/schemas/import-logs.ts
πŸ“š Learning: in apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward conditi...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.

Applied to files:

  • apps/web/lib/rewardful/schemas.ts
  • apps/web/lib/actions/partners/start-rewardful-import.ts
  • apps/web/lib/partnerstack/schemas.ts
  • apps/web/lib/rewardful/import-partners.ts
  • apps/web/lib/rewardful/import-customers.ts
  • apps/web/lib/actions/partners/create-program.ts
πŸ“š Learning: in the partnerprogramsummary email template (packages/email/src/templates/partner-program-summary.ts...
Learnt from: devkiran
PR: dubinc/dub#2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.

Applied to files:

  • apps/web/lib/partnerstack/import-commissions.ts
  • apps/web/lib/tolt/import-commissions.ts
πŸ“š Learning: in the singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in th...
Learnt from: devkiran
PR: dubinc/dub#2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.

Applied to files:

  • apps/web/lib/partnerstack/schemas.ts
πŸ“š Learning: when zod enum validation is used (e.g., `z.enum(payment_method_types)`), it ensures the validated va...
Learnt from: devkiran
PR: dubinc/dub#2433
File: apps/web/app/api/workspaces/[idOrSlug]/billing/payment-methods/route.ts:74-76
Timestamp: 2025-05-29T09:47:33.583Z
Learning: When Zod enum validation is used (e.g., `z.enum(PAYMENT_METHOD_TYPES)`), it ensures the validated value is one of the valid enum values, making subsequent type casts to the corresponding TypeScript types safe without additional validation.

Applied to files:

  • apps/web/lib/zod/schemas/import-logs.ts
πŸ“š Learning: in apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined res...
Learnt from: devkiran
PR: dubinc/dub#2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/lib/rewardful/import-partners.ts
  • apps/web/lib/rewardful/import-customers.ts
🧬 Code Graph Analysis (10)
apps/web/lib/tolt/api.ts (1)
apps/web/lib/tolt/schemas.ts (1)
  • ToltProgramSchema (21-27)
apps/web/lib/actions/partners/start-partnerstack-import.ts (1)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/partnerstack/import-commissions.ts (1)
apps/web/lib/analytics/convert-currency.ts (1)
  • convertCurrency (22-56)
apps/web/lib/actions/partners/start-rewardful-import.ts (1)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/actions/partners/start-tolt-import.ts (1)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/types.ts (1)
apps/web/lib/zod/schemas/import-logs.ts (1)
  • importLogSchema (3-20)
apps/web/lib/tinybird/get-import-logs.ts (1)
apps/web/lib/zod/schemas/import-logs.ts (1)
  • importLogSchema (3-20)
apps/web/lib/tinybird/record-import-logs.ts (1)
apps/web/lib/zod/schemas/import-logs.ts (1)
  • importLogSchema (3-20)
apps/web/lib/actions/partners/create-program.ts (3)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/tolt/importer.ts (1)
  • toltImporter (78-78)
apps/web/lib/partnerstack/importer.ts (1)
  • partnerStackImporter (47-47)
apps/web/lib/rewardful/import-commissions.ts (4)
apps/web/lib/rewardful/types.ts (1)
  • RewardfulCommission (88-100)
apps/web/lib/types.ts (1)
  • ImportLogInput (518-521)
apps/web/lib/tinybird/record-import-logs.ts (1)
  • recordImportLog (10-20)
apps/web/lib/analytics/convert-currency.ts (1)
  • convertCurrency (22-56)
πŸ”‡ Additional comments (35)
apps/web/lib/analytics/convert-currency.ts (3)

25-25: LGTM! Performance optimization with optional pre-fetched exchange rates.

This parameter allows callers to pass pre-fetched exchange rates, reducing Redis calls when processing multiple currency conversions.


31-34: Good improvement to currency handling and rate lookup.

The uppercase conversion ensures consistent currency code handling, and the nullish coalescing operator provides clean fallback logic from passed rates to Redis lookup.


36-38: Simplified conditional logic with single fxRate variable.

The change from checking fxRates?.[currencyCode] to a single fxRate variable improves readability and maintains the same functionality.

apps/web/lib/tolt/api.ts (1)

68-68: LGTM! Unnecessary object spread removed.

Clean simplification that removes redundant object spread operation while maintaining the same parsing functionality. This aligns well with the updated ToltProgramSchema that now includes the currency_code field.

apps/web/lib/rewardful/schemas.ts (2)

11-11: LGTM! Import tracking support added.

The required importId field enables consistent import operation tracking and logging across the Rewardful import system.


17-17: Good practice: Trailing comma added.

Improves maintainability for future field additions.

apps/web/lib/actions/partners/start-partnerstack-import.ts (2)

3-3: LGTM! Standard ID generation utility imported.

Proper import of the createId utility for generating unique import identifiers.


50-50: LGTM! Import tracking ID generation added.

Proper usage of createId() to generate a unique identifier for tracking this PartnerStack import operation. This enables import logging and retrieval functionality.

apps/web/lib/actions/partners/start-tolt-import.ts (2)

3-3: LGTM! Clean import addition for import tracking.

The import of createId function supports the new import logging infrastructure by enabling unique identification of import operations.


45-45: LGTM! Proper integration of import tracking.

The addition of importId using createId() enables unique identification and logging of import operations, aligning with the broader import logging infrastructure.

apps/web/lib/actions/partners/start-rewardful-import.ts (2)

3-3: LGTM! Consistent import tracking implementation.

The import addition follows the same pattern as other partner import actions, enabling unified import logging across different integrations.


37-37: LGTM! Unified import tracking approach.

The importId addition maintains consistency across partner integrations and enables comprehensive import operation tracking.

apps/web/lib/tolt/schemas.ts (2)

13-13: LGTM! Essential for import tracking.

The required importId field ensures all Tolt import operations can be uniquely identified and logged, supporting the new import logging infrastructure.


26-26: LGTM! Critical for accurate currency conversion.

The currency_code field directly supports the PR objective of ensuring amounts are accurately converted to USD by providing the source currency information needed for proper conversion calculations.

apps/web/lib/partnerstack/schemas.ts (2)

17-17: LGTM! Maintains consistency across partner integrations.

The required importId field ensures PartnerStack imports follow the same tracking pattern as other partner integrations, enabling unified import logging.


67-68: Clarify currency handling for commission amounts.

The addition of the currency field supports the PR objective of accurate USD conversion. However, there's a potential inconsistency: Line 67 describes amount as "in cents (USD)" while the new currency field suggests commissions may be in other currencies.

Please clarify whether:

  1. The amount field comment should be updated to remove the "(USD)" specification
  2. The amount field will now contain values in the currency specified by the currency field

This clarification ensures proper handling of multi-currency commission data during import and conversion processes.

apps/web/lib/tinybird/get-import-logs.ts (1)

1-12: LGTM! Well-structured import log retrieval implementation.

The implementation follows best practices:

  • Proper type validation for parameters and returned data using Zod schemas
  • Clear, descriptive parameter names (workspaceId, importId)
  • Consistent with the established Tinybird pipe pattern
  • Supports the new import logging infrastructure effectively
apps/web/lib/actions/partners/create-program.ts (1)

146-166: LGTM! Consistent implementation of import tracking.

The addition of importId generation using createId() is consistent across all three import sources (Rewardful, Tolt, and PartnerStack). This enables proper tracking and logging of import operations throughout the system.

apps/web/lib/types.ts (2)

38-38: LGTM! Proper schema import placement.

The import for importLogSchema is correctly placed with other schema imports, maintaining consistency with the file structure.


516-521: LGTM! Well-structured type definitions.

The ImportLog and ImportLogInput types are properly defined. Using Pick to create ImportLogInput as a subset of ImportLog is a good pattern for scenarios where only specific fields are needed for logging operations.

apps/web/lib/api/create-id.ts (1)

59-59: LGTM! Enables parameterless function calls.

Adding the default empty object = {} allows createId() to be called without arguments, which is exactly how it's used in the import functionality. This maintains backward compatibility while enabling the new usage pattern.

packages/email/src/templates/campaign-imported.tsx (1)

26-36: LGTM! Good addition of optional importId parameter.

The optional importId parameter with a default value is well-structured and enables flexible usage of the component.

packages/tinybird/pipes/get_import_logs.pipe (1)

8-30: LGTM! Well-structured Tinybird pipe with proper parameterization.

The SQL query correctly uses parameterized inputs with validation, includes appropriate ordering and limits for performance. The default values appear to be sample identifiers for testing purposes.

packages/tinybird/datasources/dub_import_logs.datasource (1)

4-17: Excellent datasource design with optimized performance characteristics.

The schema uses appropriate data types, the MergeTree engine with time-based partitioning is optimal for log data, and the 180-day TTL provides reasonable retention. The sorting key aligns well with expected query patterns.

apps/web/lib/partnerstack/import-commissions.ts (2)

45-46: LGTM! Proper FX rates fetching for currency conversion.

The implementation correctly fetches foreign exchange rates from Redis to enable currency normalization across commission imports.


144-171: LGTM! Well-implemented currency conversion logic.

The currency conversion logic correctly handles both sale amounts and earnings, only converting when the currency differs from USD and FX rates are available. The converted values properly replace the original amounts for all subsequent operations.

apps/web/lib/rewardful/import-partners.ts (3)

14-21: LGTM! Proper destructuring with importId for logging.

The payload destructuring correctly includes the new importId field needed for the import logging infrastructure.


59-93: Excellent implementation of partner filtering and import logging.

The logic correctly separates active partners (with leads > 0) from inactive ones, and generates structured import logs for partners that are not imported. This provides valuable visibility into why certain partners are skipped during the import process.


98-106: Well-structured bulk import logging.

The bulk logging approach is efficient and correctly enriches each log entry with workspace ID, import ID, and source information. This aligns with the logging patterns established in other import modules.

apps/web/lib/tolt/import-commissions.ts (1)

42-46: LGTM! Currency conversion setup looks good.

The function correctly fetches the program details and FX rates before processing commissions. The currency code is appropriately normalized to lowercase for consistency.

Also applies to: 68-69

apps/web/lib/rewardful/import-customers.ts (2)

15-15: Excellent structured logging implementation!

The import logging setup is well-designed with bulk recording after processing all referrals, which is more efficient than individual log writes. The metadata enrichment with workspace_id, import_id, and source provides good traceability.

Also applies to: 33-33, 61-69


113-119: Well-structured error logging!

The replacement of console.log with structured import logs is a significant improvement. The error codes (LINK_NOT_FOUND, STRIPE_CUSTOMER_ID_NOT_FOUND) are descriptive and will make debugging and monitoring much easier.

Also applies to: 127-133

apps/web/lib/rewardful/import-commissions.ts (3)

19-24: Good refactoring and consistent logging pattern!

Moving toDubStatus outside the function is a good optimization. The import logging implementation is consistent with other importers and provides proper error tracking.

Also applies to: 39-39, 44-44, 72-79


173-199: Robust currency conversion and error handling!

The currency conversion logic correctly handles both sale amounts and earnings, converting them to USD when needed. The use of converted amounts throughout the function (including in the duplicate check query) ensures data consistency. The comprehensive structured logging for all error scenarios is excellent.

Also applies to: 217-217, 318-318


149-154: Comprehensive structured error logging!

Excellent replacement of console.log statements with structured import logs. Each error case has a unique, descriptive code that will make debugging and monitoring much more effective. The consistent logging pattern across all error scenarios is commendable.

Also applies to: 222-227, 242-247, 252-283, 290-295

@devkiran
Copy link
Collaborator Author

devkiran commented Aug 3, 2025

@CodeRabbit full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 3, 2025

βœ… Actions performed

Full review triggered.

Copy link
Contributor

@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: 6

🧹 Nitpick comments (2)
packages/tinybird/datasources/dub_import_logs.datasource (1)

4-17: Well-designed datasource for import logging.

The schema and engine configuration are appropriate for time-series logging data. The monthly partitioning, sorting key, and 180-day TTL are well-suited for debugging and tracking import issues.

Consider adding compression settings for storage efficiency:

 ENGINE "MergeTree"
+ENGINE_SETTINGS "compress_method=LZ4"
 ENGINE_PARTITION_KEY "toYYYYMM(timestamp)"
 ENGINE_SORTING_KEY "timestamp, workspace_id, import_id"
 ENGINE_TTL "toDateTime(timestamp) + toIntervalDay(180)"
apps/web/lib/rewardful/import-commissions.ts (1)

252-283: Consider consolidating validation checks.

The separate validation checks for linkId, clickId, and partnerId create multiple similar log entries. Consider combining these checks for cleaner code.

-  if (!customerFound.linkId) {
-    importLogs.push({
-      entity: "commission",
-      entity_id: commission.id,
-      code: "LINK_NOT_FOUND",
-      message: `No link found for customer ${customerFound.id}.`,
-    });
-
-    return;
-  }
-
-  if (!customerFound.clickId) {
-    importLogs.push({
-      entity: "commission",
-      entity_id: commission.id,
-      code: "CLICK_NOT_FOUND",
-      message: `No click ID found for customer ${customerFound.id}.`,
-    });
-
-    return;
-  }
-
-  if (!customerFound.link?.partnerId) {
-    importLogs.push({
-      entity: "commission",
-      entity_id: commission.id,
-      code: "PARTNER_NOT_FOUND",
-      message: `No partner ID found for customer ${customerFound.id}.`,
-    });
-
-    return;
-  }
+  const missingFields = [];
+  if (!customerFound.linkId) missingFields.push("link");
+  if (!customerFound.clickId) missingFields.push("click ID");
+  if (!customerFound.link?.partnerId) missingFields.push("partner ID");
+
+  if (missingFields.length > 0) {
+    importLogs.push({
+      entity: "commission",
+      entity_id: commission.id,
+      code: "MISSING_REQUIRED_FIELDS",
+      message: `Missing ${missingFields.join(", ")} for customer ${customerFound.id}.`,
+    });
+    return;
+  }
πŸ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 7798a39 and 52120a8.

πŸ“’ Files selected for processing (24)
  • apps/web/app/api/workspaces/[idOrSlug]/import/[importId]/download/route.ts (1 hunks)
  • apps/web/lib/actions/partners/create-program.ts (1 hunks)
  • apps/web/lib/actions/partners/start-partnerstack-import.ts (2 hunks)
  • apps/web/lib/actions/partners/start-rewardful-import.ts (2 hunks)
  • apps/web/lib/actions/partners/start-tolt-import.ts (2 hunks)
  • apps/web/lib/analytics/convert-currency.ts (1 hunks)
  • apps/web/lib/api/create-id.ts (1 hunks)
  • apps/web/lib/partnerstack/import-commissions.ts (9 hunks)
  • apps/web/lib/partnerstack/schemas.ts (2 hunks)
  • apps/web/lib/rewardful/api.ts (0 hunks)
  • apps/web/lib/rewardful/import-commissions.ts (13 hunks)
  • apps/web/lib/rewardful/import-customers.ts (7 hunks)
  • apps/web/lib/rewardful/import-partners.ts (4 hunks)
  • apps/web/lib/rewardful/schemas.ts (1 hunks)
  • apps/web/lib/tinybird/get-import-logs.ts (1 hunks)
  • apps/web/lib/tinybird/record-import-logs.ts (1 hunks)
  • apps/web/lib/tolt/api.ts (1 hunks)
  • apps/web/lib/tolt/import-commissions.ts (10 hunks)
  • apps/web/lib/tolt/schemas.ts (2 hunks)
  • apps/web/lib/types.ts (2 hunks)
  • apps/web/lib/zod/schemas/import-logs.ts (1 hunks)
  • packages/email/src/templates/campaign-imported.tsx (3 hunks)
  • packages/tinybird/datasources/dub_import_logs.datasource (1 hunks)
  • packages/tinybird/pipes/get_import_logs.pipe (1 hunks)
πŸ’€ Files with no reviewable changes (1)
  • apps/web/lib/rewardful/api.ts
🧰 Additional context used
🧠 Learnings (4)
πŸ“š Learning: in apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward conditi...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.

Applied to files:

  • apps/web/lib/actions/partners/start-rewardful-import.ts
  • apps/web/lib/rewardful/schemas.ts
  • apps/web/lib/partnerstack/schemas.ts
  • apps/web/lib/actions/partners/create-program.ts
  • apps/web/lib/rewardful/import-partners.ts
  • apps/web/lib/rewardful/import-customers.ts
  • apps/web/lib/rewardful/import-commissions.ts
πŸ“š Learning: in apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition ob...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.

Applied to files:

  • apps/web/lib/rewardful/schemas.ts
  • apps/web/lib/partnerstack/schemas.ts
  • apps/web/lib/tolt/schemas.ts
  • apps/web/lib/types.ts
  • apps/web/lib/zod/schemas/import-logs.ts
πŸ“š Learning: in apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined res...
Learnt from: devkiran
PR: dubinc/dub#2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/lib/rewardful/import-partners.ts
  • apps/web/lib/rewardful/import-customers.ts
πŸ“š Learning: in the partnerprogramsummary email template (packages/email/src/templates/partner-program-summary.ts...
Learnt from: devkiran
PR: dubinc/dub#2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.

Applied to files:

  • apps/web/lib/partnerstack/import-commissions.ts
  • apps/web/lib/tolt/import-commissions.ts
🧬 Code Graph Analysis (9)
apps/web/lib/tolt/api.ts (1)
apps/web/lib/tolt/schemas.ts (1)
  • ToltProgramSchema (21-27)
apps/web/lib/actions/partners/start-rewardful-import.ts (1)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/actions/partners/start-partnerstack-import.ts (1)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/tinybird/record-import-logs.ts (1)
apps/web/lib/zod/schemas/import-logs.ts (1)
  • importLogSchema (3-20)
apps/web/lib/actions/partners/start-tolt-import.ts (1)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/actions/partners/create-program.ts (3)
apps/web/lib/api/create-id.ts (1)
  • createId (55-64)
apps/web/lib/tolt/importer.ts (1)
  • toltImporter (78-78)
apps/web/lib/partnerstack/importer.ts (1)
  • partnerStackImporter (47-47)
apps/web/lib/types.ts (1)
apps/web/lib/zod/schemas/import-logs.ts (1)
  • importLogSchema (3-20)
apps/web/lib/tinybird/get-import-logs.ts (1)
apps/web/lib/zod/schemas/import-logs.ts (1)
  • importLogSchema (3-20)
apps/web/lib/partnerstack/import-commissions.ts (1)
apps/web/lib/analytics/convert-currency.ts (1)
  • convertCurrency (22-61)
πŸ”‡ Additional comments (31)
apps/web/lib/api/create-id.ts (1)

59-59: LGTM! Improved function safety.

Adding the default empty object parameter prevents potential destructuring errors when createId() is called without arguments, making the API more robust and user-friendly.

apps/web/lib/tolt/api.ts (1)

68-68: LGTM! Clean code simplification.

Removing the unnecessary object spread operation improves readability and performance without changing functionality. The direct parsing of partner.program is cleaner and more efficient.

apps/web/lib/rewardful/schemas.ts (1)

11-17: LGTM! Consistent import tracking enhancement.

Adding the required importId field aligns with the broader import logging infrastructure and enables linking import operations with their detailed logs. The trailing comma addition is also good practice.

apps/web/lib/actions/partners/start-partnerstack-import.ts (2)

3-3: LGTM! Proper integration of import tracking.

The import statement correctly brings in the createId utility for generating unique import identifiers.


50-50: LGTM! Consistent import ID generation.

Adding the importId using createId() enables tracking and logging for this import operation, consistent with the broader import infrastructure enhancements across the codebase.

apps/web/lib/actions/partners/start-rewardful-import.ts (1)

3-3: LGTM! Import ID tracking properly implemented.

The addition of createId import and its usage to generate a unique importId for the Rewardful import operation aligns well with the broader import tracking infrastructure being introduced across the codebase.

Also applies to: 37-37

apps/web/lib/actions/partners/start-tolt-import.ts (1)

3-3: LGTM! Consistent import ID implementation.

The changes mirror the pattern established in the Rewardful import action, correctly generating and passing a unique importId to enable import tracking and logging.

Also applies to: 45-45

apps/web/lib/tinybird/get-import-logs.ts (1)

1-12: LGTM! Well-structured Tinybird pipe implementation.

The getImportLogs function is properly implemented with:

  • Correct parameter validation using Zod schema
  • Appropriate data validation against importLogSchema
  • Clean import structure and pipe naming convention

This integrates well with the new import logging infrastructure.

apps/web/lib/tolt/schemas.ts (1)

13-13: Schema usage verifiedβ€”no action needed.

All calls to toltImportPayloadSchema include the new importId and userId fields, and every instantiation of ToltProgramSchema (parse or extend) is backed by data that includes currency_code. No missing or outdated usages were found.

apps/web/lib/actions/partners/create-program.ts (1)

146-146: LGTM! Consistent addition of import tracking.

The addition of importId to all three importer queue calls (Rewardful, Tolt, and PartnerStack) is consistent and follows the same pattern. This enables tracking of import operations across different providers, which aligns well with the structured import logging system being introduced.

Also applies to: 154-154, 162-162

apps/web/lib/types.ts (1)

38-38: LGTM! Well-structured type definitions for import logging.

The addition of ImportLog and ImportLogInput types follows good TypeScript practices:

  • ImportLog provides the complete type definition inferred from the schema
  • ImportLogInput appropriately restricts to only the fields needed for input operations
  • The types align with the importLogSchema structure shown in the relevant code snippets

Also applies to: 516-521

packages/tinybird/pipes/get_import_logs.pipe (1)

1-30: LGTM! Well-structured Tinybird pipe for import log retrieval.

The pipe implementation is solid:

  • Clear description and appropriate SQL query structure
  • Proper parameter definitions with required=True for both workspaceId and importId
  • Sensible ordering by timestamp DESC to get recent logs first
  • Reasonable limit of 5000 records to prevent performance issues
  • Good default values for testing/documentation purposes
apps/web/lib/tinybird/record-import-logs.ts (1)

5-20: LGTM! Clean and focused import log recording implementation.

The implementation follows good practices:

  • Proper Tinybird ingest endpoint configuration with the correct datasource and schema
  • Type-safe function signature using Zod schema inference
  • Efficient early return for empty arrays
  • Useful console logging for monitoring import operations
  • Clean async/await pattern

The function has a clear single responsibility and integrates well with the broader import logging system.

packages/email/src/templates/campaign-imported.tsx (1)

26-26: LGTM! Good addition of optional import tracking.

The addition of the optional importId prop with a sensible default value is well-implemented and maintains backward compatibility.

Also applies to: 36-36

apps/web/lib/zod/schemas/import-logs.ts (1)

3-20: Well-structured import log schema.

The schema design is comprehensive and follows good practices:

  • Uses enums to constrain valid values for source, entity, and code fields
  • Covers all partner integrations (rewardful, tolt, partnerstack) and entities (partner, link, customer, commission)
  • Includes specific error codes that provide clear categorization of import issues
apps/web/lib/rewardful/import-partners.ts (3)

14-21: LGTM!

The payload destructuring correctly extracts the importId needed for import logging correlation.


59-92: Good separation of active vs inactive partners with proper logging.

The filtering logic correctly identifies partners worth importing (active with leads > 0) and provides structured logging for those that are skipped. The error message is descriptive and will help with debugging import issues.


98-105: Efficient bulk logging implementation.

The bulk recording of import logs with proper context enrichment (workspace_id, import_id, source) follows best practices and integrates well with the structured logging infrastructure.

apps/web/lib/partnerstack/import-commissions.ts (4)

45-45: Efficient FX rates fetching approach.

Fetching exchange rates once per batch and reusing them for all commissions is more efficient than fetching rates individually for each commission.


89-98: LGTM!

The function signature update correctly adds the fxRates parameter with appropriate typing to support currency conversion.


144-156: Correct currency conversion implementation for sale amounts.

The conversion logic properly:

  • Checks for non-USD currency before converting
  • Only converts when fxRates are available
  • Uses the updated convertCurrency function with fxRates parameter
  • Replaces the original amount with the converted value

158-170: Consistent earnings conversion implementation.

The earnings conversion logic correctly mirrors the sale amount conversion approach, ensuring both monetary values are consistently normalized to USD when possible.

apps/web/lib/analytics/convert-currency.ts (2)

22-30: Well-designed function signature enhancement.

The addition of the optional fxRates parameter maintains backward compatibility while enabling performance optimization by allowing callers to provide pre-fetched exchange rates.


31-39: Correct implementation of fxRates prioritization and currency normalization.

The logic properly:

  • Normalizes currency code to uppercase for consistent lookups
  • Prioritizes provided fxRates over Redis lookup for better performance
  • Maintains the same fallback behavior when fxRates are not provided
apps/web/lib/rewardful/import-customers.ts (3)

6-8: Import logging implementation looks good!

The addition of structured import logging with unique import IDs provides excellent traceability for debugging import issues. The logs are properly accumulated and batch recorded at the end of processing.

Also applies to: 15-15, 33-33, 47-53, 61-69


78-78: Function rename improves clarity.

Renaming from createReferral to createCustomer better reflects the function's purpose of creating customer records.


113-119: Structured error logging is well implemented.

Replacing console.log with structured import logs provides better tracking and debugging capabilities. The log entries include all necessary fields (entity, entity_id, code, message) for comprehensive error reporting.

Also applies to: 127-133

apps/web/lib/tolt/import-commissions.ts (1)

42-45: Currency normalization implementation looks good!

The implementation properly fetches currency information from the Tolt program and converts amounts to USD using exchange rates from Redis. The converted amounts are consistently used throughout all database operations.

Also applies to: 68-69, 133-140, 183-206, 222-222, 272-273, 286-286, 288-288, 303-303, 318-318

apps/web/lib/rewardful/import-commissions.ts (3)

19-24: Good refactoring of status mapping.

Moving the toDubStatus mapping outside the function improves reusability and makes the code cleaner.


39-39: Excellent implementation of structured import logging!

The comprehensive import logging with specific error codes and detailed messages will greatly improve debugging and monitoring of import operations. The logs are properly accumulated and batch recorded for efficiency.

Also applies to: 62-63, 72-79, 149-155, 222-228, 242-248, 290-296


174-199: Currency conversion implementation is thorough.

The code properly converts both sale amounts and earnings to USD, and consistently uses the converted amounts throughout all database operations and event recording.

Also applies to: 217-217, 316-317, 330-330, 343-343, 352-352

@steven-tey
Copy link
Collaborator

@CodeRabbit final review please. Is there anything we missed during the refactoring?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 3, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@steven-tey I'll conduct a thorough final review to check for any missed items in the currency conversion refactoring. Let me examine the codebase comprehensively.


🏁 Script executed:

#!/bin/bash
# First, let's examine the currency conversion implementation and usage patterns
echo "=== Examining currency conversion usage ==="
rg "convertCurrency" -A 3 -B 2

echo -e "\n=== Checking for any hardcoded USD assumptions ==="
rg -i "usd|dollars?" -A 2 -B 2 --type ts --type tsx

echo -e "\n=== Looking for FX rates usage and potential fallbacks ==="
rg "fxRates" -A 5 -B 2

echo -e "\n=== Checking for any remaining console.log statements ==="
rg "console\.(log|error|warn)" --type ts --type tsx

Length of output: 24176


🏁 Script executed:

#!/bin/bash
# Check for database schema changes and migrations
echo "=== Looking for database schema files ==="
fd -e sql -e prisma schema migration

echo -e "\n=== Checking if currency field changes are reflected in schemas ==="
rg "currency" --type ts -A 2 -B 2 | grep -i schema

echo -e "\n=== Examining import error log schema consistency ==="
cat apps/web/lib/zod/schemas/import-error-log.ts

echo -e "\n=== Checking Tinybird datasource definition ==="
cat packages/tinybird/datasources/dub_import_error_logs.datasource

Length of output: 7850


🏁 Script executed:

#!/bin/bash
# Check for potential issues with import ID handling
echo "=== Checking all import payload schemas for importId ==="
rg "importId" --type ts -A 2 -B 2

echo -e "\n=== Looking for any missed email template references ==="
rg "CampaignImported" --type ts --type tsx

echo -e "\n=== Checking if all import functions handle importId consistently ==="
ast-grep --pattern 'function $FUNC($$$) {
  $$$
  importId
  $$$
}'

Length of output: 11844


🏁 Script executed:

#!/bin/bash
# Check for error handling and potential edge cases
echo "=== Examining error handling in currency conversion ==="
cat apps/web/lib/analytics/convert-currency.ts

echo -e "\n=== Looking for potential null/undefined handling issues ==="
rg "fxRates\?" -A 3 -B 3

echo -e "\n=== Checking for any missing try-catch blocks in import functions ==="
rg "async function.*import" -A 10 | grep -A 10 -B 10 "try\|catch"

Length of output: 2994


🏁 Script executed:

#!/bin/bash
# Check for any remaining TODOs or FIXMEs related to currency
echo "=== Looking for TODOs and FIXMEs ==="
rg -i "todo|fixme" --type ts --type tsx

echo -e "\n=== Checking for zero-decimal currency handling consistency ==="
rg "ZERO_DECIMAL_CURRENCIES\|zeroDecimalCurrencies" -A 5 -B 5

echo -e "\n=== Examining the import error codes for completeness ==="
rg "INACTIVE_PARTNER\|PARTNER_NOT_FOUND\|LINK_NOT_FOUND" -A 2 -B 2

Length of output: 559


🏁 Script executed:

#!/bin/bash
# Look for TODOs and potential issues without file type restrictions
echo "=== Looking for TODOs and FIXMEs ==="
rg -i "todo|fixme" 

echo -e "\n=== Check for console.log statements without file type restriction ==="
rg "console\.(log|error|warn)"

echo -e "\n=== Look for any missed CampaignImported references ==="
rg "CampaignImported"

echo -e "\n=== Check for hardcoded USD assumptions ==="
rg -i '"usd"'

Length of output: 77214


🏁 Script executed:

#!/bin/bash
# Check for specific potential issues in the currency conversion logic
echo "=== Examining convertCurrencyWithFxRates parameter usage ==="
rg "convertCurrencyWithFxRates" -A 5 -B 2

echo -e "\n=== Check if fxRates lookup is correct ==="
rg "fxRates\[" -A 2 -B 2

echo -e "\n=== Look for potential division by zero issues ==="
rg "Number\(fxRates" -A 3 -B 3

Length of output: 7806


🏁 Script executed:

#!/bin/bash
# Check for database schema consistency and potential migration issues
echo "=== Look for Prisma schema files ==="
fd "schema.prisma"

echo -e "\n=== Check for any database migration files ==="
fd -t f -e sql | head -10

echo -e "\n=== Look for currency field in database schemas ==="
rg "currency.*String\|currency.*Field" -A 2 -B 2

Length of output: 445


@steven-tey Based on my comprehensive final review of the currency conversion refactoring, here are the key findings and potential issues that may need attention:

βœ… What's Working Well

  1. Currency Conversion Logic: The new convertCurrency and convertCurrencyWithFxRates functions properly handle zero-decimal currencies and FX rate conversions.

  2. Import Infrastructure: All three import systems (Rewardful, Tolt, PartnerStack) consistently:

    • Generate unique importId with "import_" prefix
    • Fetch FX rates from Redis (fxRates:usd)
    • Convert amounts to USD before storing
    • Use structured error logging via Tinybird
  3. Error Logging System: Well-designed Tinybird datasource with proper TTL (180 days) and comprehensive error codes.

  4. Email Template Migration: Successfully migrated from CampaignImported to ProgramImported with download link support.

⚠️ Issues That Need Attention

1. Inconsistent Error Logging in PartnerStack

The PartnerStack import still uses console.log statements instead of structured logging:

// Found in lib/partnerstack/import-commissions.ts
console.log(`Commission ${commission.key} has no transaction, skipping...`);
console.log(`Commission ${commission.key} has no customer, skipping...`);
console.log(`Commission ${commission.key} already exists, skipping...`);
// ... and several more

Recommendation: Convert these to use logImportError like the Rewardful implementation.

2. Missing FX Rate Error Handling

The convertCurrencyWithFxRates function doesn't validate FX rates:

let convertedAmount = amount / Number(fxRates); // Potential division by zero

Recommendation: Add validation for valid numeric FX rates and handle edge cases.

3. Currency Code Inconsistency

Mixed usage of "USD" vs "usd" throughout the codebase could lead to conversion misses:

// Some places use uppercase
if (saleCurrency !== "USD" && fxRates) 
// Others use lowercase  
if (currency !== "usd") 

Recommendation: Standardize on uppercase for comparisons since convertCurrencyWithFxRates uses currency.toUpperCase().

4. Silent FX Rate Failures

When FX rates are unavailable from Redis, the system silently falls back to original amounts without logging:

if (fxRates) {
  // Convert currency
} else {
  // Silent fallback - no logging or alerting
  return { currency, amount };
}

Recommendation: Add monitoring/logging when FX rates are unavailable.

5. TODO Comments Indicate Future Work

Multiple TODO comments suggest ongoing currency-related work:

// TODO: allow custom "defaultCurrency" on workspace table in the future

πŸ”§ Immediate Action Items

  1. Fix PartnerStack Logging: Convert remaining console.log statements to structured logImportError calls
  2. Add FX Rate Validation: Implement proper error handling for invalid/missing FX rates
  3. Standardize Currency Codes: Ensure consistent uppercase usage for currency comparisons
  4. Add Monitoring: Log when FX rate conversion fails or rates are unavailable

The refactoring foundation is solid, but these issues should be addressed to ensure robust currency conversion across all import scenarios.

Copy link
Contributor

@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: 1

πŸ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 8f9ffda and 339e33b.

πŸ“’ Files selected for processing (2)
  • apps/web/scripts/bulk-delete-links.ts (1 hunks)
  • packages/email/src/templates/program-imported.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/email/src/templates/program-imported.tsx
🧰 Additional context used
🧠 Learnings (1)
πŸ“š Learning: in apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined res...
Learnt from: devkiran
PR: dubinc/dub#2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/scripts/bulk-delete-links.ts
🧬 Code Graph Analysis (1)
apps/web/scripts/bulk-delete-links.ts (1)
apps/web/lib/tinybird/record-link.ts (2)
  • recordLinkTB (46-50)
  • transformLinkTB (52-71)
⏰ 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: build
πŸ”‡ Additional comments (5)
apps/web/scripts/bulk-delete-links.ts (5)

3-3: Import path change looks good.

The change from absolute to relative import path is acceptable. Ensure this aligns with the project's import path conventions.


10-12: LGTM! Filtering change aligns with program-based approach.

The switch from domain-based to programId-based filtering is appropriate, and removing the unnecessary tags include improves query performance.


14-15: Good addition of preview logging.

The logging provides helpful visibility into the links that will be deleted. Showing the last 10 links gives a good preview without overwhelming output.


17-24: Well-structured Tinybird deletion logic.

The sequential approach of updating Tinybird first with the deleted: true flag before database deletion is good practice. This ensures audit trail preservation and data consistency.


26-34: Improved precision with ID-based deletion.

The change from domain-based to ID-based deletion is more precise and safer. It ensures only the specific links that were processed are deleted, and the logging provides good confirmation.

@steven-tey
Copy link
Collaborator

@CodeRabbit I made a few final changes, could you please review again?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 4, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if PartnerStack console.log statements have been replaced with structured logging
echo "=== Checking PartnerStack import for console.log statements ==="
rg "console\.(log|error|warn)" apps/web/lib/partnerstack/

echo -e "\n=== Looking for logImportError usage in PartnerStack ==="
rg "logImportError" apps/web/lib/partnerstack/ -A 3 -B 1

echo -e "\n=== Examining the current PartnerStack import-commissions file ==="
cat apps/web/lib/partnerstack/import-commissions.ts

Length of output: 10948


🏁 Script executed:

#!/bin/bash
# Check if FX rate validation has been added to convertCurrencyWithFxRates
echo "=== Examining convertCurrencyWithFxRates for validation improvements ==="
cat apps/web/lib/analytics/convert-currency.ts

echo -e "\n=== Check for currency code standardization ==="
rg -i '"usd"' apps/web/lib/ | head -20

echo -e "\n=== Look for any new error handling around FX rates ==="
rg "fxRates.*null\|!fxRates" apps/web/lib/ -A 3 -B 1

Length of output: 3962


🏁 Script executed:

#!/bin/bash
# Let's check the exact structure of the convertCurrencyWithFxRates function to see if validation was added
echo "=== Checking convertCurrencyWithFxRates for validation ==="
ast-grep --pattern 'export const convertCurrencyWithFxRates = ({
  $$$
}) => {
  $$$
}'

echo -e "\n=== Look for fxRates validation specifically ==="
rg "fxRates\[currencyCode\]" -A 5 -B 5

echo -e "\n=== Check if there's validation for Number(fxRates) ==="
rg "Number\(fxRates" -A 3 -B 1

Length of output: 3477


@steven-tey I've reviewed the current state after your final changes. Here's what I found:

βœ… Improvements Made

  1. Currency Code Standardization: Good progress! I can see consistent use of .toUpperCase() for currency comparisons throughout the import functions:
    if (saleCurrency.toUpperCase() !== "USD" && fxRates) 
    if (earningsCurrency.toUpperCase() !== "USD" && fxRates)

⚠️ Issues Still Remaining

1. PartnerStack Console Logging (High Priority)

The PartnerStack import still uses console.log statements instead of structured logging. I found multiple instances in import-commissions.ts:

console.log(`Commission ${commission.key} has no transaction, skipping...`);
console.log(`Commission ${commission.key} has no customer, skipping...`);
console.log(`Commission ${commission.key} already exists, skipping...`);
console.log(`No customer found for customer email ${commission.customer.email}, skipping...`);
console.log(`No link found for customer ${customer.id}, skipping...`);
console.log(`No click ID found for customer ${customer.id}, skipping...`);
console.log(`No partner ID found for customer ${customer.id}, skipping...`);
console.log(`No lead event found for customer ${customer.id}, skipping...`);

Recommendation: Replace these with structured logImportError calls like in the Rewardful implementation.

2. Missing FX Rate Validation (High Priority)

The convertCurrencyWithFxRates function still lacks validation:

const fxRates = fxRatesData[currencyCode];
// No validation here - fxRates could be undefined
let convertedAmount = amount / Number(fxRates); // Potential division by zero/undefined

Recommendation: Add validation:

const fxRate = fxRatesData[currencyCode];
if (!fxRate || isNaN(Number(fxRate)) || Number(fxRate) <= 0) {
  // Handle invalid FX rate
  return { currency, amount }; // or throw error
}
let convertedAmount = amount / Number(fxRate);

These two issues could cause:

  • Poor observability: Missing structured logs for PartnerStack import failures
  • Runtime errors: Division by zero or NaN when FX rates are invalid/missing

Would you like me to help address these remaining issues?

@steven-tey
Copy link
Collaborator

@CodeRabbit OK I updated based on your feedback for convertCurrencyWithFxRates, can you check again? You can ignore the partnerstack stuff, we'll add it later

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 4, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current implementation of convertCurrencyWithFxRates
echo "=== Examining updated convertCurrencyWithFxRates function ==="
cat apps/web/lib/analytics/convert-currency.ts

echo -e "\n=== Looking for FX rate validation patterns ==="
rg "fxRate.*isNaN\|Number.*fxRate.*<=\|!fxRate" apps/web/lib/analytics/convert-currency.ts -A 3 -B 1

echo -e "\n=== Check for error handling around FX rates ==="
rg "fxRatesData\[currencyCode\]" apps/web/lib/analytics/convert-currency.ts -A 5 -B 2

Length of output: 2557


@steven-tey I've checked the updated convertCurrencyWithFxRates function, and I can see that the FX rate validation hasn't been added yet. The function still has the same potential issue:

export const convertCurrencyWithFxRates = ({
  currency,
  amount,
  fxRates,
}: {
  currency: string;
  amount: number;
  fxRates: Record<string, string>;
}) => {
  const currencyCode = currency.toUpperCase();
  const isZeroDecimalCurrency = ZERO_DECIMAL_CURRENCIES.includes(currencyCode);

  if (fxRates) {
    const fxRate = fxRates[currencyCode];

    // ⚠️ No validation here - fxRate could be undefined, null, or invalid
    let convertedAmount = amount / Number(fxRate); // Potential division by zero/NaN
    // ...
  }
  // ...
};

The issue: fxRate could be undefined if currencyCode doesn't exist in the fxRates object, leading to Number(undefined) which equals NaN, causing amount / NaN to return NaN.

Recommended fix:

if (fxRates) {
  const fxRate = fxRates[currencyCode];
  
  // Add validation here
  if (!fxRate || isNaN(Number(fxRate)) || Number(fxRate) <= 0) {
    // Return original currency/amount if FX rate is invalid
    return { currency, amount };
  }

  let convertedAmount = amount / Number(fxRate);
  // ... rest of the logic
}

This will prevent runtime errors when FX rates are missing or invalid for specific currencies.

@steven-tey
Copy link
Collaborator

@CodeRabbit ok I fixed it, can you check one last time?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 4, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@steven-tey I'll check the updated convertCurrencyWithFxRates function to verify the FX rate validation fix.


🏁 Script executed:

#!/bin/bash
# Check the current implementation of convertCurrencyWithFxRates
echo "=== Examining updated convertCurrencyWithFxRates function ==="
cat apps/web/lib/analytics/convert-currency.ts

echo -e "\n=== Looking for specific validation patterns ==="
rg "fxRate.*!.*\||isNaN.*fxRate|Number.*fxRate.*<=.*0" apps/web/lib/analytics/convert-currency.ts -A 3 -B 1

echo -e "\n=== Check the logic flow around FX rate usage ==="
rg "const fxRate.*=.*fxRates" apps/web/lib/analytics/convert-currency.ts -A 10 -B 2

Length of output: 3356


@steven-tey Perfect! βœ… The FX rate validation has been properly implemented.

The convertCurrencyWithFxRates function now includes the crucial validation:

const fxRate = fxRates[currencyCode];

// if the FX rate is not found, we return the original amount
if (!fxRate) {
  return {
    currency,
    amount,
  };
}

This fix:

  • βœ… Prevents runtime errors by checking if fxRate exists before using it
  • βœ… Handles missing currencies gracefully by returning original values as fallback
  • βœ… Avoids division by zero/NaN that could occur with Number(undefined)
  • βœ… Maintains consistency with the async convertCurrency function's validation pattern

πŸŽ‰ Currency Conversion Refactoring Complete

The refactoring now provides a robust foundation for accurate currency conversion to USD across all import systems (Rewardful, Tolt, and PartnerStack). The key improvements include:

  1. Proper FX rate validation and error handling
  2. Consistent currency code standardization
  3. Comprehensive import error logging infrastructure
  4. Zero-decimal currency support
  5. Structured logging with Tinybird integration

Great work on addressing all the critical issues! The currency conversion system is now production-ready and maintainable.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@steven-tey steven-tey merged commit ac82184 into main Aug 4, 2025
8 checks passed
@steven-tey steven-tey deleted the importer-handle-currency branch August 4, 2025 00:58
@coderabbitai coderabbitai bot mentioned this pull request Aug 14, 2025
@coderabbitai coderabbitai bot mentioned this pull request Aug 25, 2025
This was referenced Sep 9, 2025
@coderabbitai coderabbitai bot mentioned this pull request Oct 2, 2025
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.

3 participants