Skip to content

Comments

validator: currency converter#627

Merged
bangarang merged 7 commits intomainfrom
feat/CurrencyConverter
Oct 10, 2024
Merged

validator: currency converter#627
bangarang merged 7 commits intomainfrom
feat/CurrencyConverter

Conversation

@bangarang
Copy link
Collaborator

  • chore: add /validators folder
  • koala: initial commit

Please explain how to summarize this PR for the Changelog:

Tell code reviewer how and what to test:

@flatfile-nullify
Copy link

flatfile-nullify bot commented Sep 30, 2024

Nullify Code Vulnerabilities

2 findings found in this pull request

🔴 CRITICAL 🟡 HIGH 🔵 MEDIUM ⚪ LOW
0 2 0 0

You can find a list of all findings here

@bangarang bangarang force-pushed the feat/CurrencyConverter branch from 8bdcbdd to 9931774 Compare October 10, 2024 19:26
@bangarang bangarang marked this pull request as ready for review October 10, 2024 19:44
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2024

Walkthrough

The pull request introduces a currency conversion plugin for Flatfile, including comprehensive documentation, unit tests, and configuration files for Jest and Rollup. The plugin leverages the Open Exchange Rates API for real-time and historical currency conversion. Key components include validation functions, error handling, and integration with Flatfile listeners. The changes also enhance the FlatfileListener by adding the currency conversion functionality and a new workbook structure.

Changes

File Path Change Summary
convert/currency/README.MD New documentation file for the currency conversion plugin, detailing features, configuration, and usage.
convert/currency/jest.config.js New Jest configuration file specifying test environment, setup files, and timeout settings.
convert/currency/rollup.config.mjs New Rollup configuration file that exports a build configuration.
convert/currency/src/convert.currency.plugin.spec.ts New unit tests for the currency conversion plugin's utility functions using Jest.
convert/currency/src/convert.currency.plugin.ts New plugin file defining interfaces, functions, and error handling for currency conversion.
convert/currency/src/index.ts Updated to export all entities from the currency plugin module.
flatfilers/sandbox/src/index.ts Enhanced with the integration of the currencyConverterPlugin and the configureSpace plugin.

Suggested reviewers

  • carlbrugger

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

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

🧹 Outside diff range and nitpick comments (12)
convert/currency/jest.config.js (3)

12-12: Consider reducing the test timeout or separating long-running tests.

The testTimeout is set to 60 seconds, which is quite long for unit tests. While this may be necessary for integration tests or tests involving network requests, it could mask performance issues in unit tests.

Consider the following suggestions:

  1. Reduce the timeout for quick unit tests.
  2. Separate long-running tests into a different test suite with its own configuration.
  3. Use Jest's test.setTimeout() for specific long-running tests instead of a global setting.

Example of using test.setTimeout() for a specific test:

test('long running test', async () => {
  jest.setTimeout(60000); // Set timeout for this test only
  // Test code here
});

14-14: Reconsider using forceExit: true.

While forceExit: true ensures Jest exits after tests complete, it might mask issues with asynchronous operations not being properly closed. This could lead to difficulty in identifying and fixing resource leaks or unclosed handles.

Consider the following alternatives:

  1. Remove this setting and ensure all asynchronous operations are properly closed in your tests.
  2. Use --detectOpenHandles flag with Jest to identify which handles are keeping the process open.
  3. If forceExit is necessary, add a comment explaining why it's needed for this specific project.

Example of using --detectOpenHandles:

"scripts": {
  "test": "jest --detectOpenHandles"
}

15-15: Reconsider using passWithNoTests: true.

While passWithNoTests: true can be useful during initial setup or active test development, it might mask issues where tests are accidentally skipped or not detected. This could lead to false positives in your CI/CD pipeline.

Consider the following alternatives:

  1. Remove this setting once you have a stable set of tests.
  2. Use this setting only in specific environments or scripts where it's necessary.
  3. If this setting is required, add a comment explaining why it's needed for this specific project.

Example of using this setting only in a specific script:

"scripts": {
  "test:init": "jest --passWithNoTests"
}
flatfilers/sandbox/src/index.ts (2)

9-18: LGTM: Currency converter plugin is well-configured

The currencyConverterPlugin is correctly configured with appropriate options. The field mappings align with the fields defined in the space configuration, and the source (USD) and target (EUR) currencies are clearly specified.

Consider making the source and target currencies configurable through environment variables or a configuration file. This would allow for easier currency changes without modifying the code.

Example:

currencyConverterPlugin({
  // ... other options
  sourceCurrency: process.env.SOURCE_CURRENCY || 'USD',
  targetCurrency: process.env.TARGET_CURRENCY || 'EUR',
  // ...
})

19-50: LGTM: Space configuration is well-structured

The configureSpace plugin is correctly used to define the workbook and sheet structure. The configuration aligns well with the currency converter plugin setup, using matching field names and appropriate data types.

Consider adding validation rules to the field definitions to ensure data integrity. For example:

{
  key: 'amount',
  type: 'number',
  label: 'Amount',
  validators: [
    { validate: 'required' },
    { validate: 'min', value: 0 }
  ]
}

This would ensure that the amount is always provided and is non-negative.

convert/currency/src/convert.currency.plugin.spec.ts (5)

19-32: LGTM with a suggestion: validateDate test suite is well-structured.

The test suite for validateDate covers the main scenarios including valid input, empty input (defaulting to today's date), and invalid date format. However, there's a potential for improvement:

Consider mocking the current date in the test for empty input to avoid potential issues if the test runs close to midnight. This will make the test more deterministic. For example:

it('should return today\'s date if no date is provided', () => {
  const mockToday = '2023-05-15';
  jest.spyOn(global.Date, 'now').mockImplementation(() => new Date(mockToday).valueOf());
  expect(validateDate('')).toEqual({ value: mockToday });
});

Don't forget to restore the original Date.now after the test.


34-39: LGTM with suggestions: convertCurrency test suite is good but could be more comprehensive.

The test suite for convertCurrency correctly uses toBeCloseTo for floating-point comparisons and covers basic functionality. However, there's room for improvement:

Consider adding more test cases to cover edge cases and potential error scenarios:

  1. Test with very large numbers to check for potential overflow issues.
  2. Test with very small numbers to check for potential underflow or precision issues.
  3. Test with negative numbers if the function should support them.
  4. Test with zero as the amount or exchange rate to ensure proper handling.

For example:

it('should handle edge cases correctly', () => {
  expect(convertCurrency(1000000000, 1, 0.5)).toBeCloseTo(500000000, 4);
  expect(convertCurrency(0.000001, 1, 2)).toBeCloseTo(0.000002, 9);
  expect(convertCurrency(-100, 1, 0.5)).toBeCloseTo(-50, 4);
  expect(convertCurrency(100, 1, 0)).toBe(0);
});

41-46: LGTM with suggestions: calculateExchangeRate test suite is good but could be expanded.

The test suite for calculateExchangeRate correctly uses toBeCloseTo for floating-point comparisons and covers basic functionality. However, there's room for improvement:

Consider adding more test cases to cover a wider range of scenarios:

  1. Test with exchange rates that are very close to 1 to check precision.
  2. Test with very large and very small exchange rates.
  3. Test with the same currency (both rates equal to 1).

For example:

it('should handle various exchange rate scenarios', () => {
  expect(calculateExchangeRate(1, 0.99999)).toBeCloseTo(0.99999, 6);
  expect(calculateExchangeRate(1000, 0.001)).toBeCloseTo(0.000001, 9);
  expect(calculateExchangeRate(1, 1)).toBe(1);
});

48-111: LGTM with suggestions: fetchExchangeRates test suite is well-structured and comprehensive.

The test suite for fetchExchangeRates effectively uses mocking to simulate API responses and covers the main scenarios including fetching latest rates, historical rates, and handling API errors. The error handling test is particularly good practice. However, there's room for some enhancements:

Consider adding more error scenarios to ensure robust error handling:

  1. Test with a network error (e.g., timeout or connection failure).
  2. Test with an invalid API response format.
  3. Test with missing required fields in the API response.

For example:

it('should handle network errors', async () => {
  (fetch as jest.MockedFunction<typeof fetch>).mockRejectedValue(new Error('Network error'));
  await expect(fetchExchangeRates(mockApiKey, mockSourceCurrency, mockTargetCurrency))
    .rejects.toThrow('Network error');
});

it('should handle invalid API response format', async () => {
  const mockResponse = {
    ok: true,
    json: jest.fn().mockResolvedValue({ invalid: 'response' }),
  };
  (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValue(mockResponse as any);
  await expect(fetchExchangeRates(mockApiKey, mockSourceCurrency, mockTargetCurrency))
    .rejects.toThrow('Invalid API response format');
});

Also, consider testing with different combinations of source and target currencies to ensure the function handles various currency pairs correctly.


49-49: Consider using a more secure approach for the API key in tests.

While using a hardcoded API key ('test-api-key') in unit tests is common, especially when mocking API calls, it's worth considering a more secure approach:

  1. Use a clearly fake API key in tests (e.g., 'TEST_API_KEY_FOR_MOCKING') to make it obvious that it's not a real key.
  2. Alternatively, you could use an environment variable as suggested in the past comments, which would align the test setup more closely with production practices:
const mockApiKey = process.env.TEST_API_KEY || 'TEST_API_KEY_FOR_MOCKING';

This approach allows for flexibility in different test environments while maintaining security.

convert/currency/README.MD (2)

22-46: Adjust heading levels for consistency

The Parameters section provides comprehensive information about each configuration option. However, the individual parameter headings are currently using h4 (####) instead of h3 (###). This inconsistency should be addressed to improve the document structure.

Please update the heading levels for all parameters. Here's an example of how to fix the first parameter:

-#### `sheetSlug` - `string` - (required)
+### `sheetSlug` - `string` - (required)

Apply this change to all parameter headings in this section.

🧰 Tools
🪛 Markdownlint

24-24: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


48-83: Replace emphasis with proper headings

The Usage section provides valuable information for setting up and using the plugin. However, some subsections are using emphasis (bold) instead of proper headings. This should be adjusted for better document structure and consistency.

Please replace the emphasized text with appropriate headings. Here's how to fix the affected lines:

-**Environment Variables**
+### Environment Variables

-**install**
+### Installation

-**import**
+### Import

-**listener.js**
+### Listener Configuration

These changes will improve the document's structure and make it easier to navigate.

🧰 Tools
🪛 Markdownlint

50-50: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


56-56: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


61-61: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


67-67: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f119b04 and da9b695.

⛔ Files ignored due to path filters (3)
  • convert/currency/metadata.json is excluded by !**/*.json
  • convert/currency/package.json is excluded by !**/*.json
  • package-lock.json is excluded by !**/package-lock.json, !**/*.json
📒 Files selected for processing (7)
  • convert/currency/README.MD (1 hunks)
  • convert/currency/jest.config.js (1 hunks)
  • convert/currency/rollup.config.mjs (1 hunks)
  • convert/currency/src/convert.currency.plugin.spec.ts (1 hunks)
  • convert/currency/src/convert.currency.plugin.ts (1 hunks)
  • convert/currency/src/index.ts (1 hunks)
  • flatfilers/sandbox/src/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • convert/currency/rollup.config.mjs
  • convert/currency/src/index.ts
🧰 Additional context used
🪛 Markdownlint
convert/currency/README.MD

24-24: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


50-50: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


56-56: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


61-61: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


67-67: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🪛 Biome
convert/currency/src/convert.currency.plugin.ts

[error] 37-37: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

🔇 Additional comments (16)
convert/currency/jest.config.js (6)

2-2: LGTM: Appropriate test environment set.

The testEnvironment is correctly set to 'node', which is suitable for a Node.js project. This ensures that tests run in a Node.js environment, which is appropriate for server-side JavaScript code.


4-6: LGTM: Correct TypeScript transformation setup.

The transform setting is properly configured to use 'ts-jest' for TypeScript files. This ensures that both .ts and .tsx files are correctly transformed before running tests, which is essential for TypeScript projects.


1-16: Overall, a solid Jest configuration with some areas for potential improvement.

This Jest configuration file provides a good foundation for testing the currency conversion plugin. It correctly sets up the Node.js environment, handles TypeScript transformation, and includes necessary setup files. However, consider the following suggestions for improvement:

  1. Reevaluate the long testTimeout of 60 seconds, possibly separating long-running tests.
  2. Reconsider the use of forceExit: true, as it might mask issues with unclosed handles.
  3. Review the necessity of passWithNoTests: true once you have a stable set of tests.

These adjustments could lead to more robust and efficient testing as your project matures. Don't forget to verify the existence and contents of the referenced setup files as suggested in the previous comments.


8-11: LGTM: Appropriate test setup and cleanup files included.

The setupFilesAfterEnv array includes files for enhancing console output and handling test cleanup, which are good practices for maintaining a clean test environment and improving test debugging.

Verify the existence and contents of the setup files:

#!/bin/bash
# Description: Verify the existence and contents of the setup files.

# Test: Check if the files exist and display their contents
for file in "test/betterConsoleLog.js" "test/unit.cleanup.js"; do
    if [ -f "$file" ]; then
        echo "File $file exists. Contents:"
        cat "$file"
        echo "----------------------"
    else
        echo "File $file does not exist."
    fi
done

13-13: LGTM: Global setup file included.

The globalSetup correctly points to a setup file that will run once before all tests. This is good practice for performing one-time setup operations needed across all tests.

Verify the existence and contents of the global setup file:

#!/bin/bash
# Description: Verify the existence and contents of the global setup file.

# Test: Check if the file exists and display its contents
if [ -f "test/setup-global.js" ]; then
    echo "File exists. Contents:"
    cat "test/setup-global.js"
else
    echo "File does not exist."
fi

7-7: LGTM: Environment setup file included.

The setupFiles array correctly includes a dotenv configuration file, which is good practice for managing environment variables in tests.

Verify the existence and contents of the dotenv configuration file:

✅ Verification successful

Verified: setupFiles is correctly configured.

The setupFiles array includes the existing dotenv-config.js, ensuring environment variables are properly loaded during tests.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and contents of the dotenv configuration file.

# Test: Check if the file exists and display its contents
if [ -f "test/dotenv-config.js" ]; then
    echo "File exists. Contents:"
    cat "test/dotenv-config.js"
else
    echo "File does not exist."
fi

Length of output: 454


Script:

#!/bin/bash
# Description: Verify the existence and contents of the dotenv configuration file.

# Test: Check if the file exists and display its contents
if [ -f "test/dotenv-config.js" ]; then
    echo "File exists. Contents:"
    cat "test/dotenv-config.js"
else
    echo "File does not exist."
fi

Length of output: 325

flatfilers/sandbox/src/index.ts (2)

1-4: LGTM: Imports are correctly added

The new imports for currencyConverterPlugin and configureSpace are correctly added and align with the new functionality introduced in the file. The import statements follow the correct syntax and naming conventions.


6-8: LGTM: Default export function structure is maintained

The default export function structure is correctly maintained. The new plugin usages are added appropriately using the listener.use() method, preserving the async nature of the function.

convert/currency/src/convert.currency.plugin.spec.ts (3)

1-4: LGTM: Imports and mock setup are appropriate.

The import statements correctly bring in the necessary functions for testing, and mocking 'cross-fetch' is a good practice for isolating tests from actual network calls.


6-17: LGTM: validateAmount test suite is comprehensive.

The test suite for validateAmount covers both valid and invalid inputs, including edge cases like empty strings and non-numeric inputs. The expectations are clear and align with the expected behavior of the function.


1-112: Overall, excellent test coverage with room for minor enhancements.

This test file provides comprehensive coverage for the currency converter plugin's main functions. The use of Jest features and mocking techniques is appropriate and effective. The test cases cover the primary scenarios for each function, including error handling.

While the current tests are solid, consider implementing the suggested enhancements to cover more edge cases and error scenarios. This will further improve the robustness of your test suite and potentially catch subtle bugs.

Great job on prioritizing thorough testing for this plugin!

convert/currency/README.MD (4)

1-11: LGTM: Clear and informative introduction

The introductory section provides a concise overview of the plugin, its purpose, and key technical details. The inclusion of the event type and supported field types is particularly helpful for developers.


13-20: LGTM: Comprehensive feature list

The Features section effectively communicates the plugin's capabilities, highlighting its flexibility and robustness. This information is valuable for potential users to understand the plugin's functionality at a glance.


85-131: LGTM: Excellent example and explanation

The Example section provides a clear and comprehensive demonstration of how to use the plugin. The included code snippet, along with the step-by-step explanation of the plugin's workflow, offers valuable guidance for developers implementing this solution. The note about the Open Exchange Rates API subscription is an important detail to include.


1-131: Overall: Excellent documentation with minor structural improvements needed

This README provides comprehensive and well-organized documentation for the @flatfile/plugin-convert-currency plugin. It effectively covers all necessary aspects including features, parameters, usage instructions, and a detailed example.

To further improve the document:

  1. Adjust the heading levels in the Parameters section to use h3 (###) instead of h4 (####) for consistency.
  2. Replace emphasized text with proper headings in the Usage section for better structure.

These minor adjustments will enhance the document's structure and readability, making it even more valuable for developers using this plugin.

Great job on creating such informative and user-friendly documentation!

🧰 Tools
🪛 Markdownlint

24-24: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


50-50: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


56-56: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


61-61: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


67-67: null
Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

convert/currency/src/convert.currency.plugin.ts (1)

74-74: Potential SSRF vulnerability due to unvalidated user input in URL construction

The fetch function uses fullUrl, which includes user-controlled inputs (sourceCurrency, targetCurrency). Without proper validation, this can lead to SSRF attacks.


const queryString = new URLSearchParams(params).toString()
const fullUrl = `${apiUrl}?${queryString}`
console.log(`Fetching exchange rates from ${fullUrl}`)
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

Avoid logging sensitive information to prevent API key leakage

Logging fullUrl includes the API key in the logs, which poses a security risk. Unauthorized access to logs could compromise the API key. Consider removing or sanitizing this log statement.

Apply this diff to fix the issue:

- console.log(`Fetching exchange rates from ${fullUrl}`)
+ console.log('Fetching exchange rates')
📝 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
console.log(`Fetching exchange rates from ${fullUrl}`)
console.log('Fetching exchange rates')

if (!amount) {
return { error: 'Amount is required' }
}
if (isNaN(amount)) {
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

Use Number.isNaN instead of isNaN to prevent unsafe type coercion

The use of isNaN(amount) can lead to unexpected results due to implicit type coercion. It's safer to convert amount to a number and then use Number.isNaN().

Apply this diff to fix the issue:

- if (isNaN(amount)) {
+ if (Number.isNaN(Number(amount))) {
📝 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
if (isNaN(amount)) {
if (Number.isNaN(Number(amount))) {
🧰 Tools
🪛 Biome

[error] 37-37: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

This was referenced Oct 31, 2024
@coderabbitai coderabbitai bot mentioned this pull request Nov 25, 2024
This was referenced Feb 19, 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.

2 participants