Skip to content
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

refactor: improve Request.from_dict to have fewer exceptions to the rule #726

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

mvadari
Copy link
Collaborator

@mvadari mvadari commented Jul 11, 2024

High Level Overview of Change

This PR:

  • Refactors Request.from_dict to have fewer exceptions to the general processing, to be more future-proof
  • Adds tests for Request.from_dict (there were previously no tests)
  • Adds support to Request.from_dict for amm_info (an exception that was forgotten)
  • Adds better error handling for the amm_info RPC

Context of Change

I noticed the number of exceptions to the general case (i.e. extra if statements) in the Request.from_dict method the other day and wanted to fix it.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update CHANGELOG.md?

  • Yes

Test Plan

Added tests for untested/undertested functions, and all the new code.

@mvadari
Copy link
Collaborator Author

mvadari commented Sep 19, 2024

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Sep 19, 2024

Actions performed

Full review triggered.

Copy link
Contributor

coderabbitai bot commented Sep 19, 2024

Walkthrough

The recent updates involve modifications to the CHANGELOG.md to document the addition of the include_deleted parameter in the ledger_entry request and the removal of Python 3.7 support, making Python 3.8 the new default version. Additionally, a fix has been implemented to retrieve FeeSettings values from the latest validated ledger, replacing a hard-coded transaction cost. The changelog also summarizes earlier changes, including the introduction of new features and improvements in error handling.

Changes

Files Change Summary
CHANGELOG.md Updated to include the addition of include_deleted parameter, removal of Python 3.7 support, and a fix for retrieving FeeSettings.
xrpl/models/base_model.py Updated ABBREVIATIONS dictionary with new entries for NFT-related terms; comments clarified without altering functionality.

Possibly related PRs

  • Add include_deleted param to ledger_entry API #721: This PR introduces the include_deleted parameter to the ledger_entry API, which is directly related to the changes made in the main PR regarding the same parameter.
  • Update CHANGELOG.md #748: This PR updates the CHANGELOG.md to reflect the addition of the include_deleted parameter and the removal of Python 3.7 support, which aligns with the updates documented in the main PR.

Suggested reviewers

  • ckeshava
  • shawnxie999

🐰 In the changelog, we take a hop,
With include_deleted, we just can't stop!
Python 3.8, our new delight,
Fixing ledgers, everything feels right!
A joyful leap, we share the news,
In our rabbit hole, we can't lose! 🐇✨


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8d155b8 and 6b1074d.

📒 Files selected for processing (1)
  • xrpl/models/base_model.py (1 hunks)
🔇 Additional comments (2)
xrpl/models/base_model.py (2)

37-37: LGTM! Clear documentation improvement.

The comment clearly explains the purpose of the ABBREVIATIONS dictionary and its relationship to the snake case key convention.


43-46: LGTM! Verify usage of new abbreviations.

The new NFT and NoRipple related abbreviations follow the established pattern and are properly ordered.

Let's verify the usage of these new abbreviations in the codebase:

✅ Verification successful

New abbreviations are actively used in the codebase

The verification shows these abbreviations are well-integrated across the codebase:

  • NFT/NFToken related terms are used in multiple transaction types (nftoken_mint.py, nftoken_burn.py, etc.)
  • NFT-specific request models exist (nft_info.py, nft_history.py, etc.)
  • NoRipple is used in dedicated request models (no_ripple_check.py)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of new NFT and NoRipple related terms in the codebase

# Search for NFT related classes and fields
echo "Checking NFT related usage:"
rg -l "(?i)(NFT|NFToken)" --type py

# Search for NoRipple related classes and fields
echo -e "\nChecking NoRipple related usage:"
rg -l "(?i)NoRipple" --type py

# Search for potential JSON key conversions
echo -e "\nChecking potential JSON key conversions:"
rg -l "(?i)(nft|nftoken|noripple).*[\"']" --type py

Length of output: 3183


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.

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.

@mvadari
Copy link
Collaborator Author

mvadari commented Sep 19, 2024

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Sep 19, 2024

Actions performed

Full review triggered.

@mvadari
Copy link
Collaborator Author

mvadari commented Sep 19, 2024

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Sep 19, 2024

Actions performed

Full review triggered.

@mvadari
Copy link
Collaborator Author

mvadari commented Sep 19, 2024

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Sep 19, 2024

Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Outside diff range and nitpick comments (1)
tests/unit/models/requests/test_requests.py (1)

3-4: Consider avoiding the import of a protected member

In line 4, _DEFAULT_API_VERSION is imported from xrpl.models.requests.request. The leading underscore indicates that it's intended for internal use (a protected member). Importing protected members can lead to potential issues if the internal implementation changes.

Suggestion:

  • If possible, access _DEFAULT_API_VERSION through a public interface or define it in a constants module that is meant for external use.
  • If importing a protected member is necessary for testing purposes, consider adding a comment explaining why this import is required.
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 17aad31 and 1d67dd5.

Files selected for processing (7)
  • CHANGELOG.md (1 hunks)
  • snippets/bridge_transfer.py (1 hunks)
  • tests/unit/models/requests/test_amm_info.py (2 hunks)
  • tests/unit/models/requests/test_requests.py (1 hunks)
  • xrpl/models/base_model.py (1 hunks)
  • xrpl/models/requests/amm_info.py (2 hunks)
  • xrpl/models/requests/request.py (3 hunks)
Additional comments not posted (19)
xrpl/models/requests/amm_info.py (1)

40-46: Excellent addition of the _get_errors method to enhance validation logic!

The new _get_errors method in the AMMInfo class significantly improves the validation of the asset, asset2, and amm_account fields. By ensuring that asset and asset2 are provided together and that they are mutually exclusive with amm_account, this method helps catch potential errors early in the request handling process.

The specific error messages added in this method provide clear guidance to the caller about the expected input format, making it easier to diagnose and fix any issues.

Overall, this change enhances the robustness and reliability of the AMM information retrieval process by enforcing stricter input validation. Great work!

tests/unit/models/requests/test_amm_info.py (4)

29-33: LGTM!

The test_amount test correctly validates that providing only the amm_account parameter is a valid configuration for the AMMInfo request model.


35-41: LGTM!

The test_all_three test correctly validates that providing all three parameters (amm_account, asset, and asset2) together is an invalid configuration for the AMMInfo request model and raises an XRPLModelException.


43-47: LGTM!

The test_only_asset test correctly validates that providing only the asset parameter is an invalid configuration for the AMMInfo request model and raises an XRPLModelException.


49-53: LGTM!

The test_only_one_asset2 test correctly validates that providing only the asset2 parameter is an invalid configuration for the AMMInfo request model and raises an XRPLModelException.

snippets/bridge_transfer.py (1)

58-58: LGTM! The sleep delay should help improve code stability.

The addition of the 0.5-second sleep delay after the account creation process is a good precautionary measure to mitigate potential flakiness or timing issues that could arise if the balance retrieval is attempted immediately after the account creation.

This delay provides a buffer time for the account creation to fully propagate before attempting to retrieve the initial balance, thereby improving the stability of the code execution.

xrpl/models/requests/request.py (3)

15-15: LGTM!

The import of ABBREVIATIONS from xrpl.models.base_model looks good.


134-135: LGTM!

The TODO comment about supporting the "command" parameter and proper JSON RPC format is informative and aligns with the existing functionality in GenericRequest. It does not introduce any functional changes at the moment.


173-178: Excellent refactoring!

The modifications to the get_method class method significantly improve the parsing of method names and the resolution of the correct request class. The key benefits include:

  1. The use of the ABBREVIATIONS mapping provides a more dynamic and maintainable approach compared to the previous series of conditional checks.
  2. The parsing logic handles both abbreviated and non-abbreviated method names by capitalizing words if no abbreviation exists, making it more flexible.
  3. Checking the parsed name against xrpl.models.requests.__all__ allows for a more extensible resolution of the correct request class, addressing the concern of missing amm_info in the previous implementation.

These changes align perfectly with the PR objectives of reducing exceptions and handling amm_info by generalizing the parsing logic. The refactoring effectively addresses the maintainability and extensibility issues raised in the past review comments.

xrpl/models/base_model.py (4)

37-38: LGTM!

The comment provides clear and useful context about the purpose of the ABBREVIATIONS dictionary.


43-43: LGTM!

The addition of the "nft": "NFT" key-value pair to the ABBREVIATIONS dictionary is relevant and consistent with the purpose of the dictionary.


45-45: LGTM!

The addition of the "nfts": "NFTs" key-value pair to the ABBREVIATIONS dictionary is relevant and consistent with the purpose of the dictionary.


46-46: LGTM!

The addition of the "noripple": "NoRipple" key-value pair to the ABBREVIATIONS dictionary is relevant and consistent with the purpose of the dictionary.

CHANGELOG.md (2)

24-24: LGTM!

Adding support for amm_info to Request.from_dict aligns with the PR objectives and improves the functionality by fixing an overlooked exception.


25-25: Verify the improved error handling for amm_info.

Enhancing the error handling for amm_info aligns with the PR objectives and improves the robustness of the code.

To verify the improved error handling, consider running the following script:

Verification successful

Verification of improved error handling for amm_info complete.

The implementation of AMMInfo in xrpl/models/requests/amm_info.py shows improved error handling with specific checks for parameter combinations. The _get_errors method ensures that:

  1. Both asset and asset2 fields are provided together or neither is provided.
  2. The amm_account field is not provided along with asset and asset2 fields.

The unit tests in tests/unit/models/requests/test_amm_info.py cover various scenarios, including:

  • Valid parameter combinations
  • Invalid combinations that should raise exceptions

These tests ensure that the error handling works as expected for different input scenarios.

While no specific integration tests for amm_info error handling were found, the unit tests provide good coverage of the error cases.

The implementation and tests support the changelog's claim of "Improved erroring for amm_info" in the 3.0.0 release.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the improved error handling for `amm_info`.

# Test 1: Search for the error handling code. Expect: Relevant error handling code for `amm_info`.
rg --type python -A 5 $'amm_info.*Error'

# Test 2: Search for test cases that cover the error scenarios. Expect: Relevant test cases.
rg --type python -A 10 $'def test_.*amm_info.*error'

Length of output: 157


Script:

#!/bin/bash
# Description: Verify the improved error handling for `amm_info` and check repository structure.

# Check for Python files
echo "Checking for Python files:"
fd -e py

# Search for amm_info without file type restrictions
echo -e "\nSearching for amm_info:"
rg -i "amm_info" -A 5

# Look for test files related to amm_info
echo -e "\nSearching for test files related to amm_info:"
rg -i "test.*amm_info"

# Check CHANGELOG.md for context
echo -e "\nChecking CHANGELOG.md for context:"
cat CHANGELOG.md

Length of output: 80367


Script:

#!/bin/bash
# Description: Examine AMMInfo implementation and tests

# Check AMMInfo implementation
echo "AMMInfo implementation:"
cat xrpl/models/requests/amm_info.py

# Check unit tests for AMMInfo
echo -e "\nAMMInfo unit tests:"
cat tests/unit/models/requests/test_amm_info.py

# Look for integration tests related to amm_info error handling
echo -e "\nIntegration tests for amm_info error handling:"
rg -i "test.*amm_info.*error" tests/integration

Length of output: 3242

tests/unit/models/requests/test_requests.py (4)

20-31: Test for from_dict method of AccountTx appears correct

The test test_from_dict correctly verifies that Request.from_dict(req) instantiates an AccountTx object with the expected default values. The expected dictionary includes default parameters (binary, forward, and api_version), and the assertion confirms that obj.to_dict() matches expected.


32-36: Test for handling missing method key is appropriate

The test test_from_dict_no_method correctly checks that an XRPLModelException is raised when the input dictionary lacks the required method key. This ensures that the from_dict method validates input as expected.


37-41: Test for mismatched method in from_dict is valid

In test_from_dict_wrong_method, the test ensures that Fee.from_dict(req) raises an XRPLModelException when the method does not match the expected value for the Fee class. This is an important validation to prevent incorrect instantiation.


93-101: Test for GenericRequest instantiation is accurate

The test_from_dict_generic_request method correctly verifies that a GenericRequest is instantiated when using Request.from_dict(req) with a method not explicitly handled by other request classes. This ensures that generic requests are appropriately managed.

Comment on lines +16 to +18
req = GenericRequest(command=command).to_dict()
expected = {**req, "api_version": _DEFAULT_API_VERSION}
self.assertDictEqual(req, expected)
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 19, 2024

Choose a reason for hiding this comment

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

Review the correctness of the test assertion

In this test, req is converted to a dictionary, and then expected is created by merging req with {"api_version": _DEFAULT_API_VERSION}:

req = GenericRequest(command=command).to_dict()
expected = {**req, "api_version": _DEFAULT_API_VERSION}
self.assertDictEqual(req, expected)

However, since expected adds api_version to req, and then you're asserting that req and expected are equal, this assertion will fail unless api_version is already included in req. This might indicate an issue with the test logic.

Suggestion:

  • Verify whether req already includes api_version. If it does, adding it again in expected is redundant.

  • If req does not include api_version, the assertion will fail. Consider modifying the test to include api_version in req before the assertion, or adjust the expected dictionary accordingly.

  • Alternatively, if the goal is to ensure that api_version is present in req, you might want to assert that directly:

    self.assertIn("api_version", req)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

api_version is added in the to_dict function.

Copy link
Contributor

Choose a reason for hiding this comment

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

@mvadari, thank you for clarifying. Since api_version is added in the to_dict function, the test assertion is correct. I'll keep this in mind for future reviews.


✏️ Learnings added
Learnt from: mvadari
PR: XRPLF/xrpl-py#726
File: tests/unit/models/requests/test_requests.py:16-18
Timestamp: 2024-11-01T20:40:21.201Z
Learning: In the xrpl-py codebase, the `to_dict` method of classes inheriting from `Request` automatically includes the `api_version` key in the resulting dictionary. When reviewing tests that use `to_dict()`, expect `api_version` to be present in the output.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

tests/unit/models/requests/test_requests.py Show resolved Hide resolved
tests/unit/models/requests/test_requests.py Show resolved Hide resolved
@mvadari mvadari changed the title refactor: improve Request.from_dict to have fewer exceptions refactor: improve Request.from_dict to have fewer exceptions to the rule Oct 8, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
CHANGELOG.md (2)

Line range hint 1-24: Consider improving the changelog format.

The changelog follows the Keep a Changelog format but could be improved:

  1. The "Unreleased" section should be at the top (which it is)
  2. Each change should start with a verb in the imperative mood (e.g., "Add" instead of "Added", "Remove" instead of "Remove")
  3. Breaking changes should be marked with "!" after the type (e.g., "### Changed!")

Consider applying these format improvements in future entries. Example:

 ## [[Unreleased]]
 
 ### Added
-- Add `include_deleted` to ledger_entry request
+- Add `include_deleted` to ledger_entry request
 
-### BREAKING CHANGE:
+### Changed!
 - Remove Python 3.7 support to fix dependency installation and use 3.8 as new default.

Future release date detected in CHANGELOG.md

The version 3.0.0 is listed with a future date (2024-07-16) which is incorrect for a released version. The version number is also present in pyproject.toml, indicating this is indeed the current version, but it should have a past release date.

  • CHANGELOG.md: Update the release date for version 3.0.0 to reflect the actual release date
  • pyproject.toml confirms version 3.0.0 is the current version
🔗 Analysis chain

Line range hint 1-24: Verify version numbers and dates.

The changelog shows version 3.0.0 as the latest released version, dated 2024-07-16. This appears to be a future date.

Let's verify the version numbers and dates:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the version numbers and dates are consistent with the repository

# Test 1: Check if there are any other files that mention the version number
echo "Checking for version numbers in other files..."
rg -i "version.*3\.0\.0|3\.0\.0.*version"

# Test 2: Check if the date is mentioned elsewhere
echo "Checking for the future date..."
rg "2024-07-16"

Length of output: 305

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1d67dd5 and 8d155b8.

📒 Files selected for processing (1)
  • CHANGELOG.md (1 hunks)

Comment on lines +30 to +31
- Added support for `amm_info` to `Request.from_dict`
- Improved erroring for `amm_info`
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 missing changelog entries for the main PR changes.

While the additions for amm_info support are documented, the changelog is missing entries for the following significant changes mentioned in the PR objectives:

  1. Refactoring of Request.from_dict to reduce exceptions
  2. Addition of tests for Request.from_dict

Consider adding these under appropriate sections (e.g., "Changed" and "Added" respectively).

Apply this diff to add the missing entries:

 ### Fixed
 - Allow empty strings for the purpose of removing fields in DIDSet transaction
 - Added support for `amm_info` to `Request.from_dict`
 - Improved erroring for `amm_info`
+
+### Changed
+- Refactored `Request.from_dict` to reduce the number of exceptions
+
+### Added
+- Added test coverage for `Request.from_dict` method
📝 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
- Added support for `amm_info` to `Request.from_dict`
- Improved erroring for `amm_info`
- Added support for `amm_info` to `Request.from_dict`
- Improved erroring for `amm_info`
### Changed
- Refactored `Request.from_dict` to reduce the number of exceptions
### Added
- Added test coverage for `Request.from_dict` method

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