Skip to content

[Components] plaid - new components #16586

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2025
Merged

[Components] plaid - new components #16586

merged 1 commit into from
May 14, 2025

Conversation

jcortes
Copy link
Collaborator

@jcortes jcortes commented May 7, 2025

WHY

Resolves #15144

Summary by CodeRabbit

  • New Features

    • Introduced multiple Plaid integration actions: create access token, create sandbox public token, get real-time balances, get transactions, and create user.
    • Added webhook event sources for instant notifications on new accounts, sync updates, and general Plaid events.
    • Enhanced Plaid app integration with full Plaid SDK support, detailed input properties, robust error handling, and pagination.
    • Added utility modules for constants, sandbox institutions, and helper functions.
    • Included test event data for webhook event simulations.
  • Chores

    • Updated package dependencies and version for improved compatibility.

@jcortes jcortes self-assigned this May 7, 2025
Copy link

vercel bot commented May 7, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Ignored (Inspect) Visit Preview May 14, 2025 3:14pm
pipedream-docs ⬜️ Ignored (Inspect) May 14, 2025 3:14pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) May 14, 2025 3:14pm

Copy link
Contributor

coderabbitai bot commented May 7, 2025

"""

Walkthrough

This update introduces a comprehensive Plaid integration, adding new Plaid API actions, webhook sources, utility modules, and constants. The Plaid app component is refactored to use the official SDK, with robust prop definitions and methods for API interaction, error handling, and pagination. Webhook sources and test events are implemented for real-time event handling.

Changes

Files / Grouped Files Change Summary
components/plaid/plaid.app.mjs Refactored to integrate the official Plaid SDK, added detailed propDefinitions, and implemented methods for Plaid API endpoints, error handling, and pagination.
components/plaid/actions/create-access-token/create-access-token.mjs
components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs
components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs
components/plaid/actions/get-transactions/get-transactions.mjs
components/plaid/actions/create-user/create-user.mjs
Added five new action modules: exchange public_token for access_token, create sandbox public_token, get real-time balance, get transactions, and create user. Each defines input properties and calls Plaid API methods via the app component.
components/plaid/common/constants.mjs
components/plaid/common/sandbox-institutions.mjs
components/plaid/common/utils.mjs
Introduced new modules for constants (limits), sandbox institution definitions, and utility functions for iteration and nested property access.
components/plaid/sources/common/events.mjs Added a module exporting webhook type and code constants for use in event handling.
components/plaid/sources/common/webhook.mjs Added a generic Plaid webhook source component with activation, event processing, and customizable hooks for metadata and event relevance.
components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs
components/plaid/sources/new-accounts-available-instant/test-event.mjs
New webhook source and test event for "New Accounts Available" events, using shared webhook logic and event constants.
components/plaid/sources/new-event-instant/new-event-instant.mjs
components/plaid/sources/new-event-instant/test-event.mjs
New webhook source and test event for generic Plaid item/status change events, with custom event metadata generation.
components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs
components/plaid/sources/sync-updates-available-instant/test-event.mjs
New webhook source and test event for "Sync Updates Available" events, including event relevance filtering and metadata generation.
components/plaid/package.json Updated Plaid component version to 0.7.0, upgraded @pipedream/platform dependency, and added Plaid SDK dependency.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Action
    participant PlaidApp
    participant PlaidAPI

    User->>Action: Trigger action (e.g., get-transactions)
    Action->>PlaidApp: Call method (e.g., getTransactions)
    PlaidApp->>PlaidAPI: Make API request
    PlaidAPI-->>PlaidApp: Return API response
    PlaidApp-->>Action: Return processed data
    Action-->>User: Output result
Loading
sequenceDiagram
    participant PlaidAPI
    participant WebhookSource
    participant System

    PlaidAPI-->>WebhookSource: Send webhook event
    WebhookSource->>WebhookSource: Check event relevance
    WebhookSource->>System: Emit event with metadata
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement webhook sources: new-accounts-available-instant, new-event-instant, sync-updates-available-instant (#15144)
Implement actions: create-access-token, create-sandbox-public-token, get-real-time-balance, get-transactions (#15144)

Suggested labels

action, trigger / source

Poem

🐇 In fields of code where Plaid does play,
Tokens exchanged and balances sway.
New accounts arrive, events take flight,
Transactions dance in data's light.
With every webhook, a tale to tell,
This rabbit hops, and hops quite well!
🌿✨
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

components/plaid/actions/create-access-token/create-access-token.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

components/plaid/actions/create-user/create-user.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 14 others

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

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 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.

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: 4

♻️ Duplicate comments (1)
components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs (1)

24-30: Same deduplication concern as other source

See the note about using event_id instead of Date.now() for deterministic IDs. Applying the same change here keeps the sources consistent.

🧹 Nitpick comments (18)
components/plaid/common/constants.mjs (1)

4-7: Consider Named Exports for Tree-Shaking
Using a default export object may hinder tree-shaking. Consider switching to named exports:

-export default {
-  DEFAULT_LIMIT,
-  DEFAULT_MAX,
-};
+export { DEFAULT_LIMIT, DEFAULT_MAX };
components/plaid/common/sandbox-institutions.mjs (3)

1-3: Add File-Level Documentation
This file defines a large static map of sandbox institutions. Adding a JSDoc header or inline comment explaining its purpose and usage will improve developer onboarding and maintainability.


2-21: Group Institutions by Category
The non-OAuth banks (INS_109508–INS_109512) would benefit from a comment block separating them from other categories. For example:

+// ── Non-OAuth sandbox institutions ────────────────────────────────────────
 INS_109508: { … },
 INS_109509: { … },
 …

22-70: Add Section Comments for Clarity
Institutions span Canadian, UK, OAuth, and degraded/unsupported cases. Adding comments before each logical group will make future updates easier to navigate.

components/plaid/sources/common/events.mjs (3)

1-5: Freeze WEBHOOK_TYPE for Immutability
Protect these constants from unintended mutation by applying Object.freeze. E.g.:

-const WEBHOOK_TYPE = { … };
+const WEBHOOK_TYPE = Object.freeze({ … });

7-26: Freeze WEBHOOK_CODE and Nested Objects
Similarly, lock down the nested WEBHOOK_CODE object and its sub-objects:

-const WEBHOOK_CODE = { … };
+const WEBHOOK_CODE = Object.freeze({ … });

28-31: Consider Named Exports over Default Export
Using named exports (export { WEBHOOK_TYPE, WEBHOOK_CODE };) improves tree-shaking and makes individual constants easier to import directly.

components/plaid/package.json (1)

16-17: Verify Dependency Versions and Compatibility
Ensure the newly added plaid@^33.0.0 SDK and upgraded @pipedream/platform@^3.0.3 are compatible and available. You can run:

npm view plaid@^33.0.0 version
npm view @pipedream/platform@^3.0.3 version
components/plaid/sources/new-accounts-available-instant/test-event.mjs (1)

8-8: Remove stray line number.

There appears to be a stray character/number "8" at the end of the file that should be removed.

  "webhook_type": "ITEM"
};
-8
components/plaid/common/utils.mjs (1)

1-17: Consider adding JSDoc comments to utility functions.

Adding JSDoc comments would improve code maintainability by providing clear documentation about parameters, return values, and usage examples.

+/**
+ * Collects all items from an async iterable into an array
+ * @param {AsyncIterable} iterations - The async iterable to collect from
+ * @returns {Promise<Array>} Array containing all items from the iterable
+ */
async function iterate(iterations) {
  const items = [];
  for await (const item of iterations) {
    items.push(item);
  }
  return items;
}

+/**
+ * Safely accesses nested properties in an object using a dot-separated path
+ * @param {Object} obj - The object to access properties from
+ * @param {string} propertyString - Dot-separated path to the property (e.g., "user.address.zipCode")
+ * @returns {*} The value at the specified path or undefined if the path doesn't exist
+ */
function getNestedProperty(obj, propertyString) {
  const properties = propertyString.split(".");
  return properties.reduce((prev, curr) => prev?.[curr], obj);
}
components/plaid/actions/get-transactions/get-transactions.mjs (3)

68-80: Consider simplifying conditional options handling.

The conditional logic for building the options object is well-structured, but could be more concise.

Consider using object property shorthand when the property name matches the variable name:

- if (includeOriginalDescription !== undefined) {
-   options.include_original_description = includeOriginalDescription;
- }
+ if (includeOriginalDescription !== undefined) {
+   options.include_original_description = includeOriginalDescription;
+ }

While the implementation is correct, this is just a stylistic suggestion.


88-91: Simplify conditional spread for options.

The conditional spread operator with logical AND could be simplified for better readability.

- ...(Object.keys(options).length > 0 && {
-   options,
- }),
+ ...(Object.keys(options).length ? { options } : {}),

This alternative is more explicit about the intent and avoids potential confusion with the logical AND operator.


95-96: Consider handling empty transactions array more directly.

The current implementation handles the possibility of transactions being undefined, which may not be necessary if the paginate method always returns an array.

- const transactionCount = transactions?.length || 0;
+ const transactionCount = transactions.length;

If there's a possibility that transactions could be undefined or null from the paginate method, then the current implementation is correct. Otherwise, this simplification would be cleaner.

components/plaid/sources/common/webhook.mjs (3)

61-62: Remove console.log statement in production code.

Console logging the createLinkToken response may not be appropriate for production code.

- console.log("createLinkToken response", response);

Consider using a debug log level if you need to retain this for troubleshooting, or remove it entirely for production code.


85-87: Uncomment or remove HTTP response handling.

There are commented-out lines related to HTTP response handling. These should either be uncommented or removed to maintain clean code.

Either uncomment and use:

http.respond({
  status: 200,
});

Or remove the commented code entirely if it's not needed.


71-73: Document the intention of isEventRelevant method.

The isEventRelevant method always returns true, which means all webhook events will be processed by default. This is likely intended to be overridden by extending components.

Add a comment to clarify the intention:

isEventRelevant() {
+  // Base implementation accepts all events; override in extending components to filter events
  return true;
},
components/plaid/sources/new-event-instant/new-event-instant.mjs (1)

14-21: Consider using a more unique event ID.

The current ID generation uses a timestamp (Date.now()), which could potentially cause collisions if multiple events are processed at the exact same millisecond.

Consider incorporating the webhook type and code into the ID for better uniqueness:

generateMeta(resource) {
  const ts = Date.now();
  return {
-   id: ts,
+   id: `${resource.webhook_type}.${resource.webhook_code}-${ts}`,
    summary: `New Event: ${resource.webhook_type}.${resource.webhook_code}`,
    ts,
  };
},

This would ensure that even if two different events arrive at the same millisecond, they would still have unique IDs.

components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs (1)

20-27: Use webhook-provided identifiers for deterministic deduping

generateMeta relies on Date.now() for both id and ts. When two webhooks arrive in the same millisecond the IDs will collide, causing dropped events in “unique” dedupe mode.
Plaid webhook payloads include an event_id; prefer that when available, falling back to the timestamp only if not present:

generateMeta(resource) {
-  const ts = Date.now();
-  return {
-    id: ts,
-    summary: `New Event: ${resource.webhook_type}.${resource.webhook_code}`,
-    ts,
-  };
+  const ts = Date.now();
+  return {
+    id: resource.event_id ?? ts,
+    summary: `New Event: ${resource.webhook_type}.${resource.webhook_code}`,
+    ts,
+  };
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b36acd5 and d876bb2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • components/plaid/actions/create-access-token/create-access-token.mjs (1 hunks)
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs (1 hunks)
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs (1 hunks)
  • components/plaid/actions/get-transactions/get-transactions.mjs (1 hunks)
  • components/plaid/common/constants.mjs (1 hunks)
  • components/plaid/common/sandbox-institutions.mjs (1 hunks)
  • components/plaid/common/utils.mjs (1 hunks)
  • components/plaid/package.json (2 hunks)
  • components/plaid/plaid.app.mjs (1 hunks)
  • components/plaid/sources/common/events.mjs (1 hunks)
  • components/plaid/sources/common/webhook.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (17)
components/plaid/common/constants.mjs (1)

1-2: Constants Naming and Purpose Clear
The DEFAULT_LIMIT and DEFAULT_MAX constants follow standard uppercase naming and appropriately centralize pagination settings.

components/plaid/package.json (1)

3-3: Version Bump Looks Good
Updating to 0.7.0 correctly reflects the addition of new features; this follows semantic versioning for a minor release.

components/plaid/sources/sync-updates-available-instant/test-event.mjs (1)

1-8: Test Fixture Matches Event Schema
The structure (environment, flags, item_id, webhook_code, webhook_type) aligns with the webhook event constants. This test event will correctly simulate a "SYNC_UPDATES_AVAILABLE" transactions webhook.

components/plaid/sources/new-event-instant/test-event.mjs (1)

1-8: Test event looks good and follows Plaid webhook format.

The exported object correctly models a Plaid webhook event with the appropriate fields needed for testing the "New Event (Instant)" source. The payload includes all required fields for a webhook update acknowledgment event.

components/plaid/sources/new-accounts-available-instant/test-event.mjs (1)

1-7: Test event structure looks good for NEW_ACCOUNTS_AVAILABLE webhook.

The payload correctly structures a Plaid webhook event for new accounts becoming available, with all the necessary fields.

components/plaid/common/utils.mjs (2)

1-7: Good implementation of async iteration utility.

The iterate function correctly collects all items from an async iterable into an array, which is useful for handling paginated responses from the Plaid API.


9-12: Effective utility for safely accessing nested properties.

The getNestedProperty function uses optional chaining to safely access nested object properties, which is ideal for handling complex nested response structures from Plaid.

components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs (2)

9-29: Props are well-defined with appropriate descriptions.

The props include the required access token and optional account IDs with clear labels and descriptions. The dynamic account ID options based on the access token provide a good user experience.


4-8: Version suggests this is a new component.

The version "0.0.1" indicates this is the first release of this component. Ensure adequate testing has been performed, especially with the Plaid sandbox environment.

Have you tested this component with real Plaid sandbox credentials to verify it works as expected?

components/plaid/actions/create-access-token/create-access-token.mjs (1)

1-32: Good implementation of the Plaid token exchange action.

This action properly implements the exchange of a public token for an access token using the Plaid API. The structure follows Pipedream's conventions with clear property definitions and a straightforward implementation.

components/plaid/actions/get-transactions/get-transactions.mjs (1)

1-100: Overall well-structured transaction retrieval action.

The action correctly implements transaction retrieval with proper pagination support and parameter handling. The code is well-organized with appropriate property definitions and validation rules.

components/plaid/sources/common/webhook.mjs (2)

54-58: Review hardcoded products array vs. configurable products props.

The webhook is configured with hardcoded products ("transactions" and "assets"), but the component also accepts product-related props that aren't being used in the Link token creation.

Consider whether the hardcoded products should be replaced with the configurable products from props:

- products: [
-   "transactions",
-   "assets",
- ],
+ products: this.products,

If the intention is to always use these specific products regardless of the props, consider removing the product-related props or adding a comment explaining why they're not used in this context.


1-93: Solid foundation for Plaid webhook event handling.

Overall, this common webhook base component provides a good foundation for handling Plaid webhook events. It correctly sets up the webhook endpoint, defines methods that can be overridden by extending components, and provides a consistent structure for event processing.

components/plaid/sources/new-event-instant/new-event-instant.mjs (1)

1-24: Clean implementation of webhook event source.

This is a well-implemented specific webhook source that correctly extends the common webhook component. It provides appropriate metadata generation for its events and includes a sample event for testing.

components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs (1)

86-95: Pass a single payload object to createSandboxPublicToken

makeRequest expects args to be the first positional argument in the SDK call, but spreading the payload (lines 86-95) directly into the wrapper call also spreads it into the options bag understood by makeRequest.
A safer pattern is:

-const response = await app.createSandboxPublicToken({
-  institution_id: institutionId,
-  initial_products: initialProducts,
-  ...(Object.keys(options).length > 0 && { options }),
-  ...(userToken && { user_token: userToken }),
-});
+const payload = {
+  institution_id: institutionId,
+  initial_products: initialProducts,
+  ...(Object.keys(options).length && { options }),
+  ...(userToken && { user_token: userToken }),
+};
+
+const response = await app.createSandboxPublicToken(payload);

This guarantees that the wrapper receives exactly the structure it forward-calls with, safeguarding against accidental argument shadowing.

components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs (1)

18-23: Guard against undefined constants

If events.WEBHOOK_CODE[webhookType] is missing (e.g. future Plaid change or typo), accessing .NEW_ACCOUNTS_AVAILABLE will throw.
Defensively test before dereferencing:

const webhookType = events.WEBHOOK_TYPE.ITEM;
-const webhookCode = events.WEBHOOK_CODE[webhookType].NEW_ACCOUNTS_AVAILABLE;
+const webhookCode =
+  events.WEBHOOK_CODE?.[webhookType]?.NEW_ACCOUNTS_AVAILABLE;

Return false early when the constant is absent to avoid runtime exceptions.

components/plaid/plaid.app.mjs (1)

217-260: Pagination loop can spin indefinitely if the API ignores offset

offset is incremented blindly (line 259) without checking whether the next page actually yielded different resources.
If Plaid ever ignores offset for a particular endpoint, the loop will never exit.
Store the last batch’s first resource ID or use the SDK’s next_cursor (for /transactions/sync-style endpoints) when available.

lcaresia
lcaresia previously approved these changes May 8, 2025
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

♻️ Duplicate comments (2)
components/plaid/plaid.app.mjs (2)

161-164: The makeRequest method has a signature mismatch issue

The plaid-node v10+ library expects methods to be called with (request, options?), but the current implementation passes args and otherOptions as separate parameters, which may lead to API call failures.


175-178: Error handling needs improvement for network failures

The current error handling will throw an exception when trying to stringify error.response.data if error.response is undefined (which happens with network errors or timeouts).

🧹 Nitpick comments (4)
components/plaid/plaid.app.mjs (2)

229-277: Pagination implementation looks robust but could be optimized

The pagination implementation via the generator function is well-structured, but it always fetches fixed-size batches (DEFAULT_LIMIT) even when the requested max is smaller. This could be optimized for cases where users only need a few records.

Consider adjusting the count parameter based on the remaining items needed:

-            options: {
-              ...resourcesFnArgs?.options,
-              count: constants.DEFAULT_LIMIT,
-              offset,
-            },
+            options: {
+              ...resourcesFnArgs?.options,
+              count: Math.min(constants.DEFAULT_LIMIT, max - resourcesCount),
+              offset,
+            },

222-227: The createUser method should be documented with parameter details

The createUser method wrapper is missing JSDoc comments describing the expected parameters, unlike some of the other methods. This would be helpful for developers using this method.

Consider adding JSDoc comments:

+    /**
+     * Creates a new user in Plaid
+     * @param {Object} args - The request parameters
+     * @param {string} args.client_user_id - A unique ID representing the end user
+     * @param {Object} [args.consumer_report_user_identity] - Optional identity information
+     * @returns {Promise<Object>} The created user object
+     */
    createUser(args = {}) {
      return this.makeRequest({
        method: "userCreate",
        ...args,
      });
    },
components/plaid/actions/create-user/create-user.mjs (2)

41-45: Consider adding validation for phone number format

While the description mentions the E.164 format for phone numbers, there's no validation to ensure users input correctly formatted numbers.

Consider adding a simple regex validation to ensure phone numbers match the E.164 format:

  phoneNumbers: {
    type: "string[]",
    label: "Phone Numbers",
    description: "The user's phone number, in E.164 format: +{countrycode}{number}. For example: `+14157452130`. Phone numbers provided in other formats will be parsed on a best-effort basis. Phone number input is validated against valid number ranges; number strings that do not match a real-world phone numbering scheme may cause the request to fail, even in the Sandbox test environment.",
+   validate: (value) => {
+     const e164Regex = /^\+[1-9]\d{1,14}$/;
+     const invalidNumbers = value.filter(num => !e164Regex.test(num.trim()));
+     if (invalidNumbers.length > 0) {
+       return `The following numbers don't match E.164 format: ${invalidNumbers.join(', ')}`;
+     }
+     return true;
+   },
  },

57-61: Add date format validation for Date of Birth

The Date of Birth field specifies a format (yyyy-mm-dd), but there's no validation to ensure users input dates in this format.

Adding validation would improve user experience by catching errors early:

  dateOfBirth: {
    type: "string",
    label: "Date of Birth",
    description: "To be provided in the format `yyyy-mm-dd`. This field is required for all Plaid Check customers.",
+   validate: (value) => {
+     const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
+     if (!dateRegex.test(value)) {
+       return "Date must be in the format yyyy-mm-dd";
+     }
+     const date = new Date(value);
+     if (isNaN(date.getTime())) {
+       return "Invalid date";
+     }
+     return true;
+   },
  },
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d876bb2 and 6f29da2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • components/plaid/actions/create-access-token/create-access-token.mjs (1 hunks)
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs (1 hunks)
  • components/plaid/actions/create-user/create-user.mjs (1 hunks)
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs (1 hunks)
  • components/plaid/actions/get-transactions/get-transactions.mjs (1 hunks)
  • components/plaid/common/constants.mjs (1 hunks)
  • components/plaid/common/sandbox-institutions.mjs (1 hunks)
  • components/plaid/common/utils.mjs (1 hunks)
  • components/plaid/package.json (2 hunks)
  • components/plaid/plaid.app.mjs (1 hunks)
  • components/plaid/sources/common/events.mjs (1 hunks)
  • components/plaid/sources/common/webhook.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/plaid/sources/new-event-instant/test-event.mjs
🚧 Files skipped from review as they are similar to previous changes (15)
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs
  • components/plaid/common/constants.mjs
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs
  • components/plaid/common/sandbox-institutions.mjs
  • components/plaid/package.json
  • components/plaid/actions/get-transactions/get-transactions.mjs
  • components/plaid/common/utils.mjs
  • components/plaid/sources/common/events.mjs
  • components/plaid/actions/create-access-token/create-access-token.mjs
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs
  • components/plaid/sources/common/webhook.mjs
  • components/plaid/sources/new-event-instant/new-event-instant.mjs
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs
🔇 Additional comments (4)
components/plaid/plaid.app.mjs (1)

11-127: Comprehensive propDefinitions for Plaid integration

The propDefinitions are well-structured with appropriate types, labels, and descriptions. The dynamic options for accountId and the extensive product lists provide a good foundation for the Plaid integration components.

components/plaid/actions/create-user/create-user.mjs (3)

3-24: Well-structured component with clear descriptions

The component is properly structured with appropriate key, name, description, and props. The toggle for consumer report identity with reloadProps: true is a good implementation of dynamic form behavior.


107-129: Well-structured request payload construction

The way the request payload is constructed, conditionally including the consumer report user identity, is clean and readable. The spread operator with the ternary expression is an elegant way to handle the conditional inclusion.


131-133: Good use of export summary and return value

The component correctly exports a human-readable summary with the created user ID and returns the full API response, following best practices for action components.

@jcortes jcortes force-pushed the plaid-new-components branch from 6f29da2 to 285b035 Compare May 14, 2025 01:13
@jcortes
Copy link
Collaborator Author

jcortes commented May 14, 2025

/approve

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: 4

♻️ Duplicate comments (3)
components/plaid/plaid.app.mjs (3)

161-164: client[method] signature mismatch risk

The Plaid SDK v10+ defines methods with the signature (request, options?), but the current implementation passes args (which contains all parameters) and otherOptions (which is always undefined with the current usage).

const client = this.getClient();
try {
-  const response = await client[method](args, otherOptions);
+  const response = await client[method](request, options);

175-178: Avoid throwing when error.response is undefined

This error handling will break if error.response is undefined, which happens with network errors or timeouts. The code will crash trying to stringify undefined.

} catch (error) {
  console.error("Error making request", error.response);
-  throw new Error(JSON.stringify(error.response.data, null, 2));
+  const safeMsg = error.response?.data
+    ? JSON.stringify(error.response.data, null, 2)
+    : error.message;
+  throw new Error(safeMsg);
}

149-150: ⚠️ Potential issue

Incorrect parameter destructuring in makeRequest method

The function signature destructures parameters incorrectly. The method parameter is part of the main object, but you're also spreading ...args which would include all other properties. This creates confusion about what parameters should be passed.

- async makeRequest({
-   method, debug = false, otherOptions, ...args
- } = {}) {
+ async makeRequest({
+   method, debug = false, request = {}, options
+ } = {}) {
🧹 Nitpick comments (2)
components/plaid/plaid.app.mjs (2)

258-263: Simplify date comparison logic

The date comparison logic can be simplified for better readability.

- const isDateGreater =
-   lastDateAt
-     && Date.parse(resource[dateField]) >= Date.parse(lastDateAt);
-
- if (!lastDateAt || isDateGreater) {
+ if (!lastDateAt || Date.parse(resource[dateField]) >= Date.parse(lastDateAt)) {
    yield resource;
    resourcesCount += 1;
  }

238-245: Avoid potential reference modification

When merging options, the current implementation might modify the original resourcesFnArgs.options reference. Use spread operator on both objects for safety.

const response =
  await resourcesFn({
    ...resourcesFnArgs,
    options: {
-     ...resourcesFnArgs?.options,
+     ...(resourcesFnArgs?.options || {}),
      count: constants.DEFAULT_LIMIT,
      offset,
    },
  });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6f29da2 and 285b035.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • components/plaid/actions/create-access-token/create-access-token.mjs (1 hunks)
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs (1 hunks)
  • components/plaid/actions/create-user/create-user.mjs (1 hunks)
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs (1 hunks)
  • components/plaid/actions/get-transactions/get-transactions.mjs (1 hunks)
  • components/plaid/common/constants.mjs (1 hunks)
  • components/plaid/common/sandbox-institutions.mjs (1 hunks)
  • components/plaid/common/utils.mjs (1 hunks)
  • components/plaid/package.json (2 hunks)
  • components/plaid/plaid.app.mjs (1 hunks)
  • components/plaid/sources/common/events.mjs (1 hunks)
  • components/plaid/sources/common/webhook.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/plaid/sources/new-event-instant/test-event.mjs
🚧 Files skipped from review as they are similar to previous changes (16)
  • components/plaid/common/constants.mjs
  • components/plaid/package.json
  • components/plaid/common/sandbox-institutions.mjs
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs
  • components/plaid/actions/get-transactions/get-transactions.mjs
  • components/plaid/actions/create-access-token/create-access-token.mjs
  • components/plaid/common/utils.mjs
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs
  • components/plaid/sources/common/webhook.mjs
  • components/plaid/sources/common/events.mjs
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs
  • components/plaid/sources/new-event-instant/new-event-instant.mjs
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs
  • components/plaid/actions/create-user/create-user.mjs
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components

@jcortes jcortes force-pushed the plaid-new-components branch from 285b035 to 9eaa060 Compare May 14, 2025 15:14
@jcortes
Copy link
Collaborator Author

jcortes commented May 14, 2025

/approve

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: 3

♻️ Duplicate comments (6)
components/plaid/plaid.app.mjs (6)

161-164: client[method] signature mismatch risk

The implementation of makeRequest method passes arguments incorrectly to the Plaid SDK methods. According to the Plaid Node SDK v10+, the method signature is (request, options?) but your implementation passes args (containing all parameters) and otherOptions (which is always undefined).

This can lead to runtime errors or unexpected behavior when calling Plaid API methods.

async makeRequest({
  method, debug = false, otherOptions, ...args
} = {}) {
  // ...
  const client = this.getClient();
  try {
-    const response = await client[method](args, otherOptions);
+    const response = await client[method](args);

Consider restructuring makeRequest to match the SDK signature:

async makeRequest({
-  method, debug = false, otherOptions, ...args
+  method, debug = false, request = {}
} = {}) {
  // ...
  const client = this.getClient();
  try {
-    const response = await client[method](args, otherOptions);
+    const response = await client[method](request);

175-177: Avoid throwing when error.response is undefined

Your error handling in makeRequest will crash when network errors occur because error.response may be undefined for network failures or timeouts, causing JSON.stringify(error.response.data) to throw another error.

} catch (error) {
  console.error("Error making request", error.response);
-  throw new Error(JSON.stringify(error.response.data, null, 2));
+  const safeMsg = error.response?.data
+    ? JSON.stringify(error.response.data, null, 2)
+    : error.message;
+  throw new Error(safeMsg);
}

129-144: Add validation for required authentication parameters

The getConf() method extracts credentials from $auth but doesn't validate their presence, which could lead to cryptic errors later if any required values are missing.

getConf() {
  const {
    client_id: clientId,
    client_secret: clientSecret,
    environment: basePath,
  } = this.$auth;
+  
+  // Validate required auth parameters
+  if (!clientId || !clientSecret || !basePath) {
+    throw new Error("Missing required authentication parameters. Please check your credentials.");
+  }
  
  return new Configuration({
    basePath,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": clientId,
        "PLAID-SECRET": clientSecret,
      },
    },
  });
}

169-173: Improve error handling for non-200 responses

Your error handling for non-200 responses logs the full response object but the error message only includes minimal information. Including more error details would make debugging easier.

if (response.status !== 200) {
  console.log("Status error", response);
-  throw new Error(`Status error: ${response.status} ${response.statusText}`);
+  const errorData = response.data ? JSON.stringify(response.data, null, 2) : '';
+  throw new Error(`Status error: ${response.status} ${response.statusText}${errorData ? `\nDetails: ${errorData}` : ''}`);
}

180-185: Refactor wrapper methods to match updated makeRequest signature

After fixing the makeRequest method signature as suggested above, all these wrapper methods need updating to pass parameters correctly.

createSandboxPublicToken(args = {}) {
  return this.makeRequest({
    method: "sandboxPublicTokenCreate",
-   ...args,
+   request: args,
  });
}

This change pattern should be applied to all wrapper methods:

  • exchangePublicToken
  • getAccountsBalance
  • getTransactions
  • getAccounts
  • createLinkToken
  • getLinkToken
  • createUser

Also applies to: 186-191, 192-197, 198-203, 204-209, 210-215, 216-221, 222-227


247-247: Add safety check for utils.getNestedProperty

The call to utils.getNestedProperty(response, resourceName) could fail if the response is undefined or not an object.

- const nextResources = utils.getNestedProperty(response, resourceName);
+ const nextResources = response ? utils.getNestedProperty(response, resourceName) : [];
🧹 Nitpick comments (3)
components/plaid/plaid.app.mjs (3)

249-252: Improve early return condition clarity for empty resources

The condition to check for empty resources is redundant with the null-check - they can be combined for clearer code.

- const nextResources = utils.getNestedProperty(response, resourceName);
- 
- if (!nextResources?.length) {
+ const nextResources = response ? utils.getNestedProperty(response, resourceName) : [];
+ 
+ if (!nextResources || nextResources.length === 0) {
  console.log("No more resources found");
  return;
}

254-267: Add resource date validation and improve variable naming

The date comparison logic using isDateGreater could be improved with better variable naming and type checking to prevent potential issues.

for (const resource of nextResources) {
-  const isDateGreater =
-    lastDateAt
-      && Date.parse(resource[dateField]) >= Date.parse(lastDateAt);
+  // Skip resources with invalid dates if filtering by date
+  if (lastDateAt && !resource[dateField]) {
+    continue;
+  }
+  
+  const resourceDate = resource[dateField] ? Date.parse(resource[dateField]) : null;
+  const filterDate = lastDateAt ? Date.parse(lastDateAt) : null;
+  const isAfterFilterDate = resourceDate && filterDate && resourceDate >= filterDate;

-  if (!lastDateAt || isDateGreater) {
+  if (!lastDateAt || isAfterFilterDate) {
    yield resource;
    resourcesCount += 1;
  }

  if (resourcesCount >= max) {
    console.log("Reached max resources");
    return;
  }
}

255-257: Improve date parsing logic in getIterations

The date parsing logic in getIterations will throw for invalid dates and doesn't handle timezone issues, which can lead to inconsistent filtering.

- const isDateGreater =
-   lastDateAt
-     && Date.parse(resource[dateField]) >= Date.parse(lastDateAt);
+ const parseDate = (dateStr) => {
+   try {
+     return dateStr ? new Date(dateStr).getTime() : null;
+   } catch (e) {
+     console.warn(`Invalid date format: ${dateStr}`);
+     return null;
+   }
+ };
+ 
+ const resourceDate = parseDate(resource[dateField]);
+ const cutoffDate = parseDate(lastDateAt);
+ 
+ const isDateGreater = lastDateAt && resourceDate && cutoffDate && resourceDate >= cutoffDate;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 285b035 and 9eaa060.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • components/plaid/actions/create-access-token/create-access-token.mjs (1 hunks)
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs (1 hunks)
  • components/plaid/actions/create-user/create-user.mjs (1 hunks)
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs (1 hunks)
  • components/plaid/actions/get-transactions/get-transactions.mjs (1 hunks)
  • components/plaid/common/constants.mjs (1 hunks)
  • components/plaid/common/sandbox-institutions.mjs (1 hunks)
  • components/plaid/common/utils.mjs (1 hunks)
  • components/plaid/package.json (2 hunks)
  • components/plaid/plaid.app.mjs (1 hunks)
  • components/plaid/sources/common/events.mjs (1 hunks)
  • components/plaid/sources/common/webhook.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs (1 hunks)
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/plaid/sources/new-event-instant/test-event.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs (1 hunks)
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
  • components/plaid/sources/sync-updates-available-instant/test-event.mjs
  • components/plaid/common/constants.mjs
  • components/plaid/package.json
  • components/plaid/sources/new-accounts-available-instant/test-event.mjs
  • components/plaid/sources/new-event-instant/test-event.mjs
  • components/plaid/sources/common/events.mjs
  • components/plaid/common/sandbox-institutions.mjs
  • components/plaid/actions/get-transactions/get-transactions.mjs
  • components/plaid/common/utils.mjs
  • components/plaid/actions/create-user/create-user.mjs
  • components/plaid/actions/get-real-time-balance/get-real-time-balance.mjs
  • components/plaid/actions/create-access-token/create-access-token.mjs
  • components/plaid/actions/create-sandbox-public-token/create-sandbox-public-token.mjs
  • components/plaid/sources/sync-updates-available-instant/sync-updates-available-instant.mjs
  • components/plaid/sources/new-event-instant/new-event-instant.mjs
  • components/plaid/sources/new-accounts-available-instant/new-accounts-available-instant.mjs
  • components/plaid/sources/common/webhook.mjs
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base

Comment on lines +228 to +277
async *getIterations({
resourcesFn, resourcesFnArgs, resourceName,
lastDateAt, dateField,
max = constants.DEFAULT_MAX,
}) {
let offset = 0;
let resourcesCount = 0;

while (true) {
const response =
await resourcesFn({
...resourcesFnArgs,
options: {
...resourcesFnArgs?.options,
count: constants.DEFAULT_LIMIT,
offset,
},
});

const nextResources = utils.getNestedProperty(response, resourceName);

if (!nextResources?.length) {
console.log("No more resources found");
return;
}

for (const resource of nextResources) {
const isDateGreater =
lastDateAt
&& Date.parse(resource[dateField]) >= Date.parse(lastDateAt);

if (!lastDateAt || isDateGreater) {
yield resource;
resourcesCount += 1;
}

if (resourcesCount >= max) {
console.log("Reached max resources");
return;
}
}

if (nextResources.length < constants.DEFAULT_LIMIT) {
console.log("No next page found");
return;
}

offset += constants.DEFAULT_LIMIT;
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to getIterations method

The getIterations method makes API calls but doesn't include try/catch blocks, which could cause the iterator to fail without providing clear error messages.

Add error handling to the getIterations method:

async *getIterations({
  resourcesFn, resourcesFnArgs, resourceName,
  lastDateAt, dateField,
  max = constants.DEFAULT_MAX,
}) {
  let offset = 0;
  let resourcesCount = 0;

  while (true) {
+   try {
      const response =
        await resourcesFn({
          ...resourcesFnArgs,
          options: {
            ...resourcesFnArgs?.options,
            count: constants.DEFAULT_LIMIT,
            offset,
          },
        });

      const nextResources = response ? utils.getNestedProperty(response, resourceName) : [];

      if (!nextResources?.length) {
        console.log("No more resources found");
        return;
      }

      for (const resource of nextResources) {
        // ... (existing code)
      }

      if (nextResources.length < constants.DEFAULT_LIMIT) {
        console.log("No next page found");
        return;
      }

      offset += constants.DEFAULT_LIMIT;
+   } catch (error) {
+     console.error("Error in getIterations:", error);
+     throw new Error(`Failed to iterate resources: ${error.message}`);
+   }
  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async *getIterations({
resourcesFn, resourcesFnArgs, resourceName,
lastDateAt, dateField,
max = constants.DEFAULT_MAX,
}) {
let offset = 0;
let resourcesCount = 0;
while (true) {
const response =
await resourcesFn({
...resourcesFnArgs,
options: {
...resourcesFnArgs?.options,
count: constants.DEFAULT_LIMIT,
offset,
},
});
const nextResources = utils.getNestedProperty(response, resourceName);
if (!nextResources?.length) {
console.log("No more resources found");
return;
}
for (const resource of nextResources) {
const isDateGreater =
lastDateAt
&& Date.parse(resource[dateField]) >= Date.parse(lastDateAt);
if (!lastDateAt || isDateGreater) {
yield resource;
resourcesCount += 1;
}
if (resourcesCount >= max) {
console.log("Reached max resources");
return;
}
}
if (nextResources.length < constants.DEFAULT_LIMIT) {
console.log("No next page found");
return;
}
offset += constants.DEFAULT_LIMIT;
}
},
async *getIterations({
resourcesFn, resourcesFnArgs, resourceName,
lastDateAt, dateField,
max = constants.DEFAULT_MAX,
}) {
let offset = 0;
let resourcesCount = 0;
while (true) {
try {
const response =
await resourcesFn({
...resourcesFnArgs,
options: {
...resourcesFnArgs?.options,
count: constants.DEFAULT_LIMIT,
offset,
},
});
const nextResources = response
? utils.getNestedProperty(response, resourceName)
: [];
if (!nextResources?.length) {
console.log("No more resources found");
return;
}
for (const resource of nextResources) {
const isDateGreater =
lastDateAt &&
Date.parse(resource[dateField]) >= Date.parse(lastDateAt);
if (!lastDateAt || isDateGreater) {
yield resource;
resourcesCount += 1;
}
if (resourcesCount >= max) {
console.log("Reached max resources");
return;
}
}
if (nextResources.length < constants.DEFAULT_LIMIT) {
console.log("No next page found");
return;
}
offset += constants.DEFAULT_LIMIT;
} catch (error) {
console.error("Error in getIterations:", error);
throw new Error(`Failed to iterate resources: ${error.message}`);
}
}
},

Comment on lines +22 to +36
async options({ accessToken }) {
if (!accessToken) {
return [];
}

const { accounts } = await this.getAccounts({
access_token: accessToken,
});
return accounts.map(({
account_id: value, name: label,
}) => ({
label,
value,
}));
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to account options function

The async options function in accountId prop definition makes an API call but doesn't include try/catch for error handling, which could silently fail and confuse users.

async options({ accessToken }) {
  if (!accessToken) {
    return [];
  }

+  try {
    const { accounts } = await this.getAccounts({
      access_token: accessToken,
    });
    return accounts.map(({
      account_id: value, name: label,
    }) => ({
      label,
      value,
    }));
+  } catch (error) {
+    console.error("Error fetching account options:", error);
+    return [];
+  }
},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async options({ accessToken }) {
if (!accessToken) {
return [];
}
const { accounts } = await this.getAccounts({
access_token: accessToken,
});
return accounts.map(({
account_id: value, name: label,
}) => ({
label,
value,
}));
},
async options({ accessToken }) {
if (!accessToken) {
return [];
}
try {
const { accounts } = await this.getAccounts({
access_token: accessToken,
});
return accounts.map(({
account_id: value, name: label,
}) => ({
label,
value,
}));
} catch (error) {
console.error("Error fetching account options:", error);
return [];
}
},

Comment on lines +148 to +160
async makeRequest({
method, debug = false, otherOptions, ...args
} = {}) {
if (debug) {
console.log("Request args", args);
console.log("Request otherOptions", otherOptions);
console.log("Request method", method);
}

if (!method) {
throw new Error("Method is required");
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve makeRequest parameter handling

The makeRequest method uses both destructured parameters and rest parameters, which makes the signature confusing. Also, the debug parameter is used for logging but there's no way to set it to true from the wrapper methods.

async makeRequest({
-  method, debug = false, otherOptions, ...args
+  method, 
+  request = {}, 
+  options = {},
+  debug = false
} = {}) {
  if (debug) {
-    console.log("Request args", args);
-    console.log("Request otherOptions", otherOptions);
+    console.log("Request payload", request);
+    console.log("Request options", options);
    console.log("Request method", method);
  }

  if (!method) {
    throw new Error("Method is required");
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async makeRequest({
method, debug = false, otherOptions, ...args
} = {}) {
if (debug) {
console.log("Request args", args);
console.log("Request otherOptions", otherOptions);
console.log("Request method", method);
}
if (!method) {
throw new Error("Method is required");
}
async makeRequest({
method,
request = {},
options = {},
debug = false
} = {}) {
if (debug) {
console.log("Request payload", request);
console.log("Request options", options);
console.log("Request method", method);
}
if (!method) {
throw new Error("Method is required");
}
// …rest of method…
}

@jcortes jcortes merged commit 0129ef7 into master May 14, 2025
11 checks passed
@jcortes jcortes deleted the plaid-new-components branch May 14, 2025 18:14
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.

[Components] plaid
2 participants