Skip to content

Conversation

jjramirezn
Copy link
Contributor

US account details form was impossible to advance due to wrong error handling and clearing, this is now fixed. Also modified a little the handling of errors for create-external-account, because on error the API doesn't return a json so it was always failing when trying to parse it

Copy link

Copy link

vercel bot commented Oct 25, 2024

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

Name Status Preview Comments Updated (UTC)
peanut-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 25, 2024 5:28pm

Copy link
Contributor

coderabbitai bot commented Oct 25, 2024

📝 Walkthrough

Walkthrough

The pull request introduces modifications to error handling in the create-external-account route and the LinkAccountComponent. It restructures how errors are managed when interacting with an external API, wrapping JSON parsing in try-catch blocks to prevent unhandled exceptions. The handling of duplicate external accounts is refined, allowing for structured error responses. Additionally, the LinkAccountComponent enhances its error management and user feedback during account linking, improving the overall user experience.

Changes

File Path Change Summary
src/app/api/bridge/external-account/create-external-account/route.ts Restructured error handling for API responses, added try-catch for JSON parsing, refined duplicate account handling.
src/components/Global/LinkAccountComponent/index.tsx Added clearErrors method to useForm, improved error state management for account linking, enhanced form submission logic.
src/utils/cashout.utils.ts Restructured error handling in createExternalAccount, added descriptive error messages, refined duplicate account handling.

Possibly related PRs

Suggested reviewers

  • panosfilianos

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

console.error('Error creating external account', e)
}
throw new Error(`HTTP error! status: ${response.status}`)
return new NextResponse('', {
Copy link
Contributor

@panosfilianos panosfilianos Oct 25, 2024

Choose a reason for hiding this comment

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

question (non-blocking): is an empty data field here enough to handle all errors to function it returns it too? wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

as an additional note that's visible in the TODOs in these diffs: we need a std way to handle these errors and propagate that error state in the whole UI. I'm currently creating a prototype of what a solution could look like in peanut-wallet.

tl;dr: don't waste too much time on this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the empty data field is not enough and I love that you are doing something about the error state, it is needed

Copy link
Contributor

Choose a reason for hiding this comment

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

cool!
this is not the scope to fix that and I have a Task and TODOs around that.

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

🧹 Outside diff range and nitpick comments (3)
src/components/Global/LinkAccountComponent/index.tsx (3)

Line range hint 89-102: Consider enhancing account number validation

The current validation logic for US account numbers uses a magic number in the regex pattern ([0-9]{6,17}). Consider:

  1. Extracting this pattern to a constant with documentation
  2. Adding more comprehensive validation for US account numbers
  3. Adding unit tests for both IBAN and US account validation paths
+ // US bank account numbers are typically between 6-17 digits
+ const US_ACCOUNT_NUMBER_PATTERN = /^[0-9]{6,17}$/;
+
  if (isIBAN(sanitizedAccountNumber)) {
      setAccountDetailsValue('type', 'iban')
- } else if (/^[0-9]{6,17}$/.test(sanitizedAccountNumber)) {
+ } else if (US_ACCOUNT_NUMBER_PATTERN.test(sanitizedAccountNumber)) {
      setAccountDetailsValue('type', 'us')
  } else {

Line range hint 134-176: Enhance error messages for better user experience

While the error handling logic is solid, consider making error messages more user-friendly and actionable:

- setErrorState({ showError: true, errorMessage: 'This account has already been linked' })
+ setErrorState({ 
+   showError: true, 
+   errorMessage: 'This account is already linked to your profile. Please use a different account or contact support if you believe this is an error.' 
+ })

  if (!response.success) {
      if (response.data && response.data.code == 'duplicate_external_account') {
-         const errorMessage = 'An external account with the same information has already been added for this customer'
+         const errorMessage = 'This account appears to be already registered. Please verify the account details or contact support for assistance.'
          throw new Error(errorMessage)
      }
-     throw new Error('Creating Bridge account failed')
+     throw new Error('Unable to link your account at this time. Please try again later or contact support if the issue persists.')
  }

Line range hint 279-372: Refactor repeated validation conditions

The form contains multiple instances of the same validation condition for US accounts. Consider extracting this logic to improve maintainability:

+ const isUsAccount = () => getAccountDetailsValue('type') === 'us';
+ const getRequiredFieldValidation = () => ({
+   required: isUsAccount() ? 'This field is required' : false
+ });
+
  <input
      {...registerAccountDetails('street', {
-         required: getAccountDetailsValue('type') === 'us' ? 'This field is required' : false,
+         ...getRequiredFieldValidation()
      })}
      className={`custom-input ${accountDetailsErrors.street ? 'border border-red' : ''}`}
      placeholder="Your street and number"
  />

This pattern can be applied to all address fields to reduce code duplication and make future updates easier.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f9b2048 and 2af999c.

📒 Files selected for processing (3)
  • src/app/api/bridge/external-account/create-external-account/route.ts (1 hunks)
  • src/components/Global/LinkAccountComponent/index.tsx (2 hunks)
  • src/utils/cashout.utils.ts (1 hunks)
🔇 Additional comments (4)
src/app/api/bridge/external-account/create-external-account/route.ts (1)

69-99: Verify US account form error handling.

Let's ensure these error handling changes properly support the US account form requirements.

✅ Verification successful

Error handling implementation is consistent and well-structured

The error handling changes in the route align well with existing patterns across the codebase:

  • The duplicate_external_account error is consistently handled in both frontend (LinkAccountComponent) and backend (cashout.utils.ts)
  • Frontend properly displays user-friendly error messages through the errorState mechanism
  • Error responses maintain the same structure across components with proper status codes and content types

The TODO comment about standardizing error responses is valid but doesn't impact current functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for US account form error handling in the frontend
rg -l "us.*account.*error" --type ts --type tsx

# Look for related test files
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx account

Length of output: 136


Script:

#!/bin/bash
# Search for US account form error handling with correct ripgrep syntax
rg -t ts "us.*account.*error"

# Look for error handling patterns in account-related files
rg -t ts "duplicate_external_account|createExternalAccount.*error"

# Find account-related test files using fd without tsx extension
fd -e ts account

# Search for error handling patterns in the frontend components
ast-grep --pattern 'catch (e) { $$$$ }'

Length of output: 779


Script:

#!/bin/bash
# Check the implementation of error handling in LinkAccountComponent
rg -A 10 "duplicate_external_account" src/components/Global/LinkAccountComponent/index.tsx

# Check the implementation in cashout utils
rg -A 10 "duplicate_external_account" src/utils/cashout.utils.ts

# Look for any other error handling patterns in these files
rg "error|Error" src/components/Global/LinkAccountComponent/index.tsx src/utils/cashout.utils.ts

Length of output: 12206

src/utils/cashout.utils.ts (2)

191-215: ⚠️ Potential issue

Improve error handling consistency and type safety.

While the error handling improvements are good, there are a few areas that need attention:

  1. The error type for duplicate accounts is currently undefined in interfaces.IResponse.
  2. Error handling is inconsistent - duplicate accounts return structured response while other errors throw.
  3. The generic "Unexpected error" message doesn't provide useful context.

Consider these improvements:

 try {
     const data = await response.json()
     if (data.code && data.code == 'duplicate_external_account') {
-        // if bridge account already exists for this iban
-        // but somehow not stored in our DB (this should never happen in
-        // prod, but can happen if same email used in prod & staging)
-        //
-        // returns:  not interfaces.IBridgeAccount type, but
-        // a currently undefined error type on the data field of interfaces.IResponse
-        // of format:
-        // {
-        //     id: '4c537540-80bf-41dd-a528-d79a4',
-        //     code: 'duplicate_external_account',
-        //     message: 'An external account with the same information has already been added for this customer'
-        // }
-        //
-        // TODO: HTTP responses need to be standardized client wide
         return {
             success: false,
             data,
         } as interfaces.IResponse
     }
+    // Handle other error cases consistently
+    return {
+        success: false,
+        data: {
+            code: data.code || 'unknown_error',
+            message: data.message || 'Failed to create external account',
+        },
+    } as interfaces.IResponse
 } catch (error) {
     console.error('Error creating external account', response)
-    throw new Error('Unexpected error')
+    return {
+        success: false,
+        data: {
+            code: 'parse_error',
+            message: 'Failed to parse error response',
+        },
+    } as interfaces.IResponse
 }

Let's verify the error type definition:

#!/bin/bash
# Search for IResponse interface definition
rg -A 10 "interface IResponse"

Consider creating a standardized error handling utility that:

  1. Defines consistent error types and responses
  2. Provides type-safe error handling functions
  3. Implements uniform error logging and reporting

218-218: 🛠️ Refactor suggestion

Add runtime type validation for the response data.

The success case assumes the response matches IBridgeAccount type without validation.

Consider adding runtime type validation:

-const data = await response.json()
+const data = await response.json()
+// Add type validation helper
+if (!isValidBridgeAccount(data)) {
+    return {
+        success: false,
+        data: {
+            code: 'invalid_response',
+            message: 'Invalid account data received',
+        },
+    } as interfaces.IResponse
+}

Let's check the IBridgeAccount interface definition:

src/components/Global/LinkAccountComponent/index.tsx (1)

57-57: LGTM: Improved form error handling

The addition of clearErrors and its usage with the country field improves the form state management by properly clearing field-specific errors instead of using undefined values.

Also applies to: 372-372

},
})
}
const data = await response.json()
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for successful response parsing.

The JSON parsing of the successful response should also be wrapped in a try-catch block to prevent unhandled exceptions.

-const data = await response.json()
+let data;
+try {
+    data = await response.json()
+} catch (e) {
+    console.error('Error parsing successful response:', e)
+    return new NextResponse('Internal Server Error', { status: 500 })
+}
📝 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
const data = await response.json()
let data;
try {
data = await response.json()
} catch (e) {
console.error('Error parsing successful response:', e)
return new NextResponse('Internal Server Error', { status: 500 })
}

Comment on lines +69 to 93
try {
const data = await response.json()
if (data.code && data.code == 'duplicate_external_account') {
// in case the bridge account already exists
//
// sending back error responses is not currently common across app
// in how we deliver errors from backend -> frontend
// sends back an object like:
// {
// id: '4c537540-80bf-41dd-a528-dbe39a4',
// code: 'duplicate_external_account',
// message: 'An external account with the same information has already been added for this customer'
// }
//
// TODO: standardize error responses across backend
return new NextResponse(JSON.stringify(data), {
status: response.status,
headers: {
'Content-Type': 'application/json',
},
})
}
} catch (e) {
console.error('Error creating external account', e)
}
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

Enhance error handling to provide more informative responses.

While the try-catch block for JSON parsing is good, the error handling could be more informative:

  1. The catch block's error logging could include the response status and URL
  2. Consider adding type safety for the error response structure
 try {
     const data = await response.json()
+    // Add type safety for error response
+    type BridgeError = {
+        id: string;
+        code: string;
+        message: string;
+    }
+    const errorData = data as BridgeError;
-    if (data.code && data.code == 'duplicate_external_account') {
+    if (errorData.code && errorData.code === 'duplicate_external_account') {
         return new NextResponse(JSON.stringify(data), {
             status: response.status,
             headers: {
                 'Content-Type': 'application/json',
             },
         })
     }
 } catch (e) {
-    console.error('Error creating external account', e)
+    console.error(`Error creating external account (status: ${response.status})`, e)
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +94 to +99
return new NextResponse('', {
status: response.status,
headers: {
'Content-Type': 'application/json',
},
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider preserving error details in non-duplicate error cases.

The current implementation returns an empty response for non-duplicate errors, which might make debugging harder for clients.

-return new NextResponse('', {
+return new NextResponse(JSON.stringify({
+    code: 'external_account_error',
+    message: `Failed to create external account (status: ${response.status})`
+}), {
     status: response.status,
     headers: {
         'Content-Type': 'application/json',
     },
 })
📝 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
return new NextResponse('', {
status: response.status,
headers: {
'Content-Type': 'application/json',
},
})
return new NextResponse(JSON.stringify({
code: 'external_account_error',
message: `Failed to create external account (status: ${response.status})`
}), {
status: response.status,
headers: {
'Content-Type': 'application/json',
},
})

@panosfilianos panosfilianos merged commit ea26b28 into develop Oct 25, 2024
2 checks passed
@jjramirezn jjramirezn deleted the fix/cashout-confirm-details branch October 25, 2024 17:49
This was referenced Oct 26, 2024
This was referenced Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants