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

feat: allow no persist for Custom Data Access #1541

Merged
merged 11 commits into from
Jan 15, 2025

Conversation

kevindavee
Copy link
Contributor

@kevindavee kevindavee commented Jan 7, 2025

Description of the changes

To use Pay before Persist feature, it depends on NoPersistHttpDataAccess and this class inherits from HttpDataAccess. If we want to use our own DataAccess, we can't use Pay Before Persist feature.

This change allows builders to use their own custom DataAccess and decide whether they want to persist it or not with the persist parameter. We no longer need NoPersistHttpDataAccess since the persistence logic will be handled in each of the DataAccess class.

If this change is accepted, we might not need #1540 .

Summary by CodeRabbit

Based on the comprehensive changes across multiple files, here are the updated release notes:

  • New Features

    • Added skipPersistence() method to data access interfaces to control transaction persistence behavior.
    • Introduced NoPersistDataWrite class for handling non-persistent transaction scenarios.
    • Added HttpDataRead and HttpDataWrite classes for enhanced HTTP data operations.
    • Introduced HttpTransaction class for managing transaction confirmations.
  • Improvements

    • Enhanced data access configuration with robust error handling and retry mechanisms.
    • Streamlined data access instantiation and persistence handling.
  • Refactoring

    • Removed NoPersistHttpDataAccess class to simplify the data access model.
    • Modularized data access classes for better separation of concerns.
  • Breaking Changes

    • Changes to data access class initialization and persistence logic may require updates in existing implementations.

Copy link
Contributor

coderabbitai bot commented Jan 7, 2025

Walkthrough

This pull request introduces significant architectural changes to the data access layer across multiple packages. The primary focus is on enhancing the flexibility and modularity of data persistence mechanisms. Key modifications include adding a skipPersistence() method to the IDataAccess interface, introducing new classes like HttpDataAccessConfig, HttpDataRead, HttpDataWrite, and NoPersistDataWrite, and removing the NoPersistHttpDataAccess class. The changes streamline how data persistence is managed and checked across different components of the request network system.

Changes

File Change Summary
packages/types/src/data-access-types.ts Added skipPersistence() method to IDataAccess interface
packages/data-access/src/combined-data-access.ts Added skipPersistence() method to check writer persistence
packages/request-client.js/src/http-data-access.ts Refactored class to extend CombinedDataAccess, added new properties and configuration handling
packages/request-client.js/src/http-metamask-data-access.ts Updated constructor and methods to use dataAccessConfig for data access
packages/request-client.js/src/http-request-network.ts Simplified data access instantiation, removed NoPersistHttpDataAccess conditional logic
packages/request-client.js/src/no-persist-http-data-access.ts Removed class handling non-persistent transactions
packages/request-client.js/src/http-data-read.ts Introduced HttpDataRead class for data retrieval operations
packages/request-client.js/src/http-data-write.ts Introduced HttpDataWrite class for data writing operations
packages/request-client.js/src/http-transaction.ts Introduced HttpTransaction class for managing transactions
packages/request-client.js/test/in-memory-request.test.ts Added tests for RequestNetwork using custom data access
packages/request-client.js/test/api/request-network.test.ts Enhanced mock implementation with skipPersistence method
packages/transaction-manager/test/index.test.ts Added skipPersistence method to fakeDataAccess for testing
packages/data-access/src/index.ts Added export for NoPersistDataWrite entity
packages/data-access/src/no-persist-data-write.ts Introduced NoPersistDataWrite class for non-persistent transaction handling
packages/request-client.js/src/http-data-access-config.ts Added HttpDataAccessConfig class for managing HTTP data access configurations

Possibly related PRs

Suggested reviewers

  • aimensahnoun
  • alexandre-abrioux
  • yomarion
  • sstefdev

📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 88fe992 and 7f36372.

📒 Files selected for processing (7)
  • packages/data-access/src/combined-data-access.ts (2 hunks)
  • packages/request-client.js/src/api/request-network.ts (4 hunks)
  • packages/request-client.js/src/http-data-access.ts (8 hunks)
  • packages/request-client.js/src/http-request-network.ts (2 hunks)
  • packages/request-client.js/test/api/request-network.test.ts (1 hunks)
  • packages/transaction-manager/test/index.test.ts (19 hunks)
  • packages/types/src/data-access-types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/types/src/data-access-types.ts
  • packages/transaction-manager/test/index.test.ts
  • packages/data-access/src/combined-data-access.ts
  • packages/request-client.js/test/api/request-network.test.ts
  • packages/request-client.js/src/api/request-network.ts
  • packages/request-client.js/src/http-request-network.ts
🧰 Additional context used
📓 Learnings (1)
packages/request-client.js/src/http-data-access.ts (2)
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-access-config.ts:76-110
Timestamp: 2025-01-10T05:15:26.574Z
Learning: When refactoring code into new classes in the Request Network codebase, maintain the existing logic and behavior without modifications to minimize risk and keep changes focused on the extraction process.
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-read.ts:34-37
Timestamp: 2025-01-10T04:25:32.038Z
Learning: In PR #1541, the HttpDataRead class was created by extracting existing logic from HttpDataAccess without adding new validations or features, to maintain a focused refactoring scope.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (5)
packages/request-client.js/src/http-data-access.ts (5)

3-14: Well-structured architectural changes!

The transition from implementing IDataAccess to extending CombinedDataAccess, along with the introduction of reader/writer components, improves modularity and separation of concerns. The property visibility modifiers are appropriately chosen.


24-28: Make the skipPersistence parameter optional in type definition

The type definition makes skipPersistence required, but the constructor provides a default value.

    }: {
      httpConfig?: Partial<ClientTypes.IHttpDataAccessConfig>;
      nodeConnectionConfig?: Partial<NodeConnectionConfig>;
-     skipPersistence: boolean;
+     skipPersistence?: boolean;
    } = {

35-46: Well-structured initialization logic!

The initialization sequence is well-organized:

  1. Creates configuration
  2. Initializes transaction handler
  3. Sets up reader/writer components
  4. Calls super with appropriate components
  5. Stores necessary references

The conditional writer selection based on skipPersistence effectively implements the "Pay before Persist" feature.


78-78: Clean delegation to reader/writer components!

The refactoring successfully maintains existing behavior while improving code organization through proper delegation to specialized components. This aligns with the learning to maintain existing logic during refactoring.

Also applies to: 88-88, 101-101, 116-116, 131-131


139-139: Verify HTTP endpoint security

The refactoring maintains proper input validation and uses the configured HTTP client. However, since these endpoints handle sensitive operations:

Also applies to: 156-158

Finishing Touches

  • 📝 Generate Docstrings (Beta)

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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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: 1

🧹 Nitpick comments (4)
packages/request-client.js/src/http-metamask-data-access.ts (1)

45-45: LGTM! Explicit persistence setting aligns with PR objectives.

The addition of persist: true maintains the existing behavior while making the persistence configuration explicit. This change supports the PR's goal of allowing custom DataAccess implementations to control persistence behavior.

Consider documenting this persistence behavior in the class's JSDoc, especially since this class handles both IPFS and Ethereum storage. This would help implementers understand the implications of the persist flag in the context of MetaMask-based storage.

packages/request-client.js/test/in-memory-request.test.ts (1)

66-84: Consider enhancing test coverage.

While the test case correctly verifies basic functionality, consider adding:

  1. Verification that the mock storage wasn't called
  2. Assertions for the actual value of mockDataAccess.persist
  3. Test case for persisting a request created with custom data access
 it('creates a request without persisting it with custom data access', async () => {
   const mockStorage = new MockStorage();
   const mockDataAccess = new MockDataAccess(mockStorage, { persist: false });
+  const mockStorageSpy = jest.spyOn(mockStorage, 'save');

   requestNetwork = new RequestNetworkBase({
     signatureProvider: TestData.fakeSignatureProvider,
     dataAccess: mockDataAccess,
   });

   const request = await requestNetwork.createRequest(requestCreationParams);

   expect(request).toBeDefined();
   expect(request.requestId).toBeDefined();
   expect(request.inMemoryInfo).toBeDefined();
   expect(request.inMemoryInfo?.requestData).toBeDefined();
   expect(request.inMemoryInfo?.topics).toBeDefined();
   expect(request.inMemoryInfo?.transactionData).toBeDefined();
   expect(spyPersistTransaction).not.toHaveBeenCalled();
+  expect(mockStorageSpy).not.toHaveBeenCalled();
+  expect(mockDataAccess.persist).toBe(false);
 });
packages/request-client.js/src/http-data-access.ts (1)

17-17: LGTM! Consider enhancing type safety.

The persist property implementation looks good with proper readonly modifier and default value.

Consider using a type alias for the constructor parameters to improve maintainability:

type HttpDataAccessConfig = {
  httpConfig?: Partial<ClientTypes.IHttpDataAccessConfig>;
  nodeConnectionConfig?: Partial<NodeConnectionConfig>;
  persist: boolean;
};

Also applies to: 38-38, 42-42, 46-46, 51-51

packages/request-client.js/src/api/request-network.ts (1)

151-151: Enhance error message clarity.

The error handling is correct, but the error message could be more specific about the current state.

Consider this improved error message:

-    if (!this.dataAccess.persist) {
-      throw new Error(
-        'Cannot persist request when skipPersistence is enabled. To persist the request, create a new instance of RequestNetwork without skipPersistence being set to true.',
-      );
-    }
+    if (!this.dataAccess.persist) {
+      throw new Error(
+        `Cannot persist request: current DataAccess instance (${this.dataAccess.constructor.name}) has persistence disabled. To persist the request, create a new instance of RequestNetwork with a DataAccess that has persistence enabled.`,
+      );
+    }

Also applies to: 160-163

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a6ddcae and 161c863.

📒 Files selected for processing (11)
  • packages/data-access/src/combined-data-access.ts (1 hunks)
  • packages/data-access/src/data-write.ts (1 hunks)
  • packages/data-access/src/mock-data-access.ts (1 hunks)
  • packages/request-client.js/src/api/request-network.ts (4 hunks)
  • packages/request-client.js/src/http-data-access.ts (2 hunks)
  • packages/request-client.js/src/http-metamask-data-access.ts (1 hunks)
  • packages/request-client.js/src/http-request-network.ts (1 hunks)
  • packages/request-client.js/src/no-persist-http-data-access.ts (1 hunks)
  • packages/request-client.js/test/in-memory-request.test.ts (2 hunks)
  • packages/thegraph-data-access/src/NoopDataWrite.ts (1 hunks)
  • packages/types/src/data-access-types.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/request-client.js/test/in-memory-request.test.ts (1)
Learnt from: MantisClone
PR: RequestNetwork/requestNetwork#1386
File: packages/request-client.js/src/api/request-network.ts:25-25
Timestamp: 2024-11-12T14:52:33.204Z
Learning: The `persistTransaction()` function in the `no-persist-http-data-access.ts` file is tested in the `in-memory-request.test.ts` file, which is separate from the typical `index.test.ts` file to avoid test interference.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (11)
packages/request-client.js/src/http-metamask-data-access.ts (1)

45-45: Verify persistence behavior across storage layers.

Since this class handles both IPFS and Ethereum storage with caching, let's verify the impact of the persistence flag:

✅ Verification successful

Persistence flag is a safe configuration setting

The persist flag is a constructor parameter passed to the parent class that doesn't impact the broader persistence behavior. The codebase shows consistent transaction persistence patterns through well-defined interfaces, with no conditional logic depending on this flag.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any derived classes and persistence-related code

# Check for classes extending HttpMetaMaskDataAccess
echo "Checking for derived implementations..."
rg "extends HttpMetaMaskDataAccess"

# Check for persistence-related code in the codebase
echo "Checking persistence-related code..."
rg "persist.*DataAccess" --type ts

# Check for IPFS/Ethereum storage patterns
echo "Checking storage patterns..."
rg "persistTransaction|getTransactionsByChannelId" --type ts -A 5

Length of output: 68901

packages/thegraph-data-access/src/NoopDataWrite.ts (1)

4-4: LGTM! Clean implementation of the persist parameter.

The addition of the persist parameter with a default value of true maintains backward compatibility while enabling the new functionality.

packages/request-client.js/src/no-persist-http-data-access.ts (1)

18-18: LGTM! Consistent implementation of no-persist behavior.

The explicit persist: false in the super call maintains the existing no-persist behavior while utilizing the new persistence control mechanism.

packages/data-access/src/mock-data-access.ts (1)

16-22: LGTM! Well-structured implementation of persistence control.

The changes properly integrate the persist option while maintaining backward compatibility through default values.

packages/data-access/src/data-write.ts (1)

9-9: Verify test coverage for persistence control.

Ensure comprehensive test coverage exists for the new persistence control functionality across different scenarios.

Let's check the test coverage:

✅ Verification successful

Test coverage for persistence control is comprehensive.

The codebase includes extensive test coverage across multiple packages, covering:

  • Core persistence functionality for both clear and encrypted transactions
  • Error handling and edge cases
  • In-memory and HTTP data access scenarios
  • API endpoint integration
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test cases related to persistence control
rg -l 'persist.*test|test.*persist' --type ts

# Look for specific test scenarios
rg -A 5 'describe.*persist|test.*persist|it.*persist' --type ts

Length of output: 26762

packages/data-access/src/combined-data-access.ts (1)

4-11: LGTM! Clean implementation of persistence state propagation.

The readonly modifier ensures immutability, and initialization from the writer's persist value maintains consistency.

packages/request-client.js/src/http-request-network.ts (1)

60-68: Verify persistence behavior across all code paths.

The implementation looks correct, but let's verify the persistence behavior is consistent across all code paths:

  1. Mock storage with skipPersistence: persist=false
  2. Mock storage without skipPersistence: persist=true
  3. NoPersistHttpDataAccess: persist=false (implicit)
  4. HttpDataAccess: persist=true (explicit)
✅ Verification successful

Persistence behavior is correctly implemented across all code paths

The implementation correctly handles persistence configuration:

  1. MockDataAccess: Properly sets persist based on skipPersistence flag
  2. NoPersistHttpDataAccess: Implicitly no persistence by design
  3. HttpDataAccess: Explicitly sets persist=true

The persistence behavior is consistent with the IDataWrite interface requirements and follows a clear pattern across all implementations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify persistence configuration is consistent
# Search for instantiations of data access classes with persist parameter
ast-grep --pattern 'new $CLASS($_, { persist: $_})'

Length of output: 464


Script:

#!/bin/bash
# Search for class definitions and persistence-related implementations
ast-grep --pattern 'class $CLASS extends $_ {
  constructor($_) {
    $$$
  }
}'

# Also search for any persistence-related method calls
rg "persist" -A 3 -B 3

Length of output: 68940

packages/types/src/data-access-types.ts (1)

28-28: LGTM! Well-placed interface addition.

The persist property is appropriately added to the IDataWrite interface, establishing a clear contract for persistence behavior.

packages/request-client.js/src/api/request-network.ts (3)

117-117: LGTM! Good decoupling.

Replacing the instanceof check with a property check improves extensibility by allowing custom DataAccess implementations to control persistence behavior.


200-200: LGTM! Consistent implementation.

The change maintains consistency with the createRequest method implementation.


Line range hint 17-17: Verify all DataAccess implementations.

Let's ensure all DataAccess implementations have been updated with the persist property.

✅ Verification successful

All DataAccess implementations correctly include the persist property

All implementations of IDataWrite and IDataAccess properly declare the readonly persist property with consistent default values. The inheritance chain is complete and no implementations are missing the required property.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all DataAccess class implementations
ast-grep --pattern 'class $_ implements DataAccessTypes.IDataAccess {
  $$$
}'

# Search for persist property declarations
rg -l "persist.*:.*boolean" packages/

Length of output: 25932


Script:

#!/bin/bash
# Check interface definition
echo "=== IDataAccess interface definition ==="
rg -A 5 "interface IDataAccess" packages/types/src/data-access-types.ts

# Look for alternative implementations
echo -e "\n=== Alternative DataAccess implementations ==="
ast-grep --pattern 'class $_ {
  $$$
  persist: boolean;
  $$$
}'

# Check content of files with persist declarations
echo -e "\n=== Content of NoopDataWrite.ts ==="
cat packages/thegraph-data-access/src/NoopDataWrite.ts

echo -e "\n=== Content of data-write.ts ==="
cat packages/data-access/src/data-write.ts

echo -e "\n=== Content of combined-data-access.ts ==="
cat packages/data-access/src/combined-data-access.ts

echo -e "\n=== Content of mock-data-access.ts ==="
cat packages/data-access/src/mock-data-access.ts

Length of output: 6687


Script:

#!/bin/bash
# Check IDataWrite interface definition
echo "=== IDataWrite interface definition ==="
rg -A 5 "interface IDataWrite" packages/types/src/data-access-types.ts

# Look for any missed implementations
echo -e "\n=== All files implementing IDataWrite or IDataAccess ==="
rg -l "implements (DataAccessTypes\.)?(IDataWrite|IDataAccess)" packages/

# Verify no other persist property implementations exist
echo -e "\n=== Any other persist property declarations ==="
rg "persist\s*:\s*boolean" packages/

Length of output: 1490

packages/data-access/src/data-write.ts Outdated Show resolved Hide resolved
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

🧹 Nitpick comments (1)
packages/request-client.js/src/http-data-access.ts (1)

97-108: Enhance type safety in result object construction

While the implementation is functionally correct, using Object.assign directly with the event emitter might not guarantee type safety. Consider creating a type guard or using a more type-safe approach.

Consider this alternative implementation:

-    const result: DataAccessTypes.IReturnPersistTransaction = Object.assign(
-      eventEmitter,
-      getNoPersistTransactionRawData(topics),
-    );
+    const noPersistData = getNoPersistTransactionRawData(topics);
+    const result: DataAccessTypes.IReturnPersistTransaction = {
+      ...noPersistData,
+      on: eventEmitter.on.bind(eventEmitter),
+      once: eventEmitter.once.bind(eventEmitter),
+      emit: eventEmitter.emit.bind(eventEmitter),
+    };

This approach:

  1. Explicitly defines the shape of the result object
  2. Properly binds event emitter methods
  3. Ensures all required properties are present
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 161c863 and ff2d53d.

📒 Files selected for processing (4)
  • packages/request-client.js/src/http-data-access.ts (5 hunks)
  • packages/request-client.js/src/http-metamask-data-access.ts (3 hunks)
  • packages/request-client.js/src/http-request-network.ts (1 hunks)
  • packages/request-client.js/src/no-persist-http-data-access.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/request-client.js/src/no-persist-http-data-access.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/request-client.js/src/http-request-network.ts
  • packages/request-client.js/src/http-metamask-data-access.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (3)
packages/request-client.js/src/http-data-access.ts (3)

7-7: LGTM!

The import of getNoPersistTransactionRawData is correctly added to support the new no-persist functionality.


18-18: LGTM!

The persist property is correctly declared as a public readonly boolean, which ensures immutability and proper type safety.


52-52: LGTM!

The persist property is correctly initialized in the constructor.

packages/request-client.js/src/http-data-access.ts Outdated Show resolved Hide resolved
@kevindavee kevindavee force-pushed the feat/allow-no-persist-for-custom-data-access branch from ff2d53d to 952184d Compare January 7, 2025 11:16
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

🧹 Nitpick comments (1)
packages/data-access/src/no-persist-transaction-raw-data.ts (1)

12-12: Consider using more precise timestamp.

The current implementation uses Date.now() / 1000 which loses precision due to floating-point division. Consider using Math.floor(Date.now() / 1000) for a more precise Unix timestamp.

-      timestamp: Date.now() / 1000,
+      timestamp: Math.floor(Date.now() / 1000),
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ff2d53d and 08b410f.

📒 Files selected for processing (9)
  • packages/data-access/src/data-write.ts (2 hunks)
  • packages/data-access/src/index.ts (1 hunks)
  • packages/data-access/src/no-persist-transaction-raw-data.ts (1 hunks)
  • packages/request-client.js/src/http-data-access.ts (5 hunks)
  • packages/request-client.js/src/http-metamask-data-access.ts (3 hunks)
  • packages/request-client.js/src/http-request-network.ts (1 hunks)
  • packages/request-client.js/src/no-persist-http-data-access.ts (0 hunks)
  • packages/request-client.js/test/api/request-network.test.ts (1 hunks)
  • packages/transaction-manager/test/index.test.ts (19 hunks)
💤 Files with no reviewable changes (1)
  • packages/request-client.js/src/no-persist-http-data-access.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/request-client.js/src/http-data-access.ts
  • packages/request-client.js/src/http-request-network.ts
🧰 Additional context used
📓 Learnings (1)
packages/request-client.js/test/api/request-network.test.ts (1)
Learnt from: MantisClone
PR: RequestNetwork/requestNetwork#1386
File: packages/request-client.js/src/api/request-network.ts:25-25
Timestamp: 2024-11-12T14:52:33.204Z
Learning: The `persistTransaction()` function in the `no-persist-http-data-access.ts` file is tested in the `in-memory-request.test.ts` file, which is separate from the typical `index.test.ts` file to avoid test interference.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (7)
packages/data-access/src/index.ts (1)

8-8: LGTM! Clean export addition.

The new export follows the established pattern in the barrel file.

packages/data-access/src/no-persist-transaction-raw-data.ts (1)

3-17: LGTM! Well-structured utility function.

The function provides a clean implementation for generating non-persisted transaction data with proper typing.

packages/request-client.js/test/api/request-network.test.ts (1)

19-19: LGTM! Consistent mock data access configuration.

The persist: true property has been consistently added to all mock data access objects throughout the test file, maintaining test consistency.

Also applies to: 230-230, 277-277, 321-321, 398-398, 450-450, 508-508, 567-567, 641-641, 729-729, 806-806, 885-885, 963-963, 1112-1112, 1187-1187, 1256-1256, 1336-1336, 1406-1406, 1486-1486

packages/transaction-manager/test/index.test.ts (1)

64-64: LGTM! Consistent mock data access configuration.

The persist: true property has been consistently added to all mock data access objects throughout the test file, maintaining test consistency.

Also applies to: 230-230, 277-277, 321-321, 398-398, 450-450, 508-508, 567-567, 641-641, 729-729, 806-806, 885-885, 963-963, 1112-1112, 1187-1187, 1256-1256, 1336-1336, 1406-1406, 1486-1486

packages/data-access/src/data-write.ts (2)

10-10: LGTM! Good default value choice.

The persist parameter with a default value of true maintains backward compatibility while allowing opt-in control of persistence.


28-39: LGTM! Clean implementation of the no-persist flow.

The implementation correctly:

  1. Creates the event emitter early
  2. Handles the no-persist case with proper event emission
  3. Returns immediately without unnecessary storage operations
packages/request-client.js/src/http-metamask-data-access.ts (1)

76-87: LGTM! Consistent implementation with DataAccessWrite.

The no-persist flow implementation matches the pattern in DataAccessWrite, ensuring consistent behavior across different DataAccess implementations.

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

🧹 Nitpick comments (1)
packages/request-client.js/src/http-metamask-data-access.ts (1)

81-90: LGTM: No-persist logic implementation

The implementation correctly:

  • Checks the persist flag
  • Uses getNoPersistTransactionRawData for consistent behavior
  • Emits confirmation immediately for no-persist cases

However, consider adding a debug log to help with troubleshooting:

    if (!this.persist) {
+     console.debug('Skipping persistence as persist is set to false');
      const result: DataAccessTypes.IReturnPersistTransaction = Object.assign(
        eventEmitter,
        getNoPersistTransactionRawData(topics),
      );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 08b410f and 3759082.

📒 Files selected for processing (2)
  • packages/request-client.js/src/http-data-access.ts (5 hunks)
  • packages/request-client.js/src/http-metamask-data-access.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/request-client.js/src/http-data-access.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (4)
packages/request-client.js/src/http-metamask-data-access.ts (4)

1-1: LGTM: Import addition aligns with new functionality

The addition of getNoPersistTransactionRawData import is appropriate for implementing the no-persist feature.


36-48: Consider deriving persist value from httpConfig

While the implementation is functional, it would be more consistent to derive the persist value from httpConfig since it's a data access configuration. This aligns with the existing pattern where HTTP-related configurations are passed through httpConfig.

-      persist,
    }: {
      httpConfig?: Partial<ClientTypes.IHttpDataAccessConfig>;
      nodeConnectionConfig?: NodeConnectionConfig;
      web3?: any;
      ethereumProviderUrl?: string;
-      persist?: boolean;
    } = {
      httpConfig: {},
-      persist: true,
    },
  ) {
-    super({ httpConfig, nodeConnectionConfig, persist });
+    super({ httpConfig, nodeConnectionConfig, persist: httpConfig?.persist ?? true });

79-80: LGTM: Event emitter initialization

The EventEmitter is correctly initialized and typed as PersistTransactionEmitter.


81-90: Verify impact on transaction tracking

Since MetaMask transactions involve blockchain interactions, ensure that downstream components correctly handle non-persisted transactions and don't assume persistence.

Run this script to check for potential assumptions about persistence in dependent components:

✅ Verification successful

Non-persisted transactions are handled safely

The implementation correctly handles non-persisted transactions by immediately emitting confirmation events and maintaining consistent behavior with persisted transactions. Downstream components are designed to work with both scenarios, as evidenced by extensive test coverage and proper handling in the transaction manager.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for code that might assume transaction persistence
echo "Checking for persistence assumptions in transaction handling..."

# Look for direct references to transaction storage or confirmation
rg -l 'transaction.*storage|confirmation.*status' --type ts

# Look for MetaMask transaction handling
ast-grep --pattern 'class $_ {
  $$$
  transaction($$$) {
    $$$
    storage.$$$
    $$$
  }
}'

Length of output: 536


Script:

#!/bin/bash
# Check transaction manager implementation
echo "=== Transaction Manager Implementation ==="
rg "class.*TransactionManager" -A 10 packages/transaction-manager/src/transaction-manager.ts

echo -e "\n=== Persistence Flag Usage ==="
rg "persist|isPersisted" --type ts -A 3

echo -e "\n=== Integration Test Cases ==="
rg "describe.*transaction" -A 5 packages/integration-test/test/layers.test.ts

Length of output: 71631

Copy link
Member

@alexandre-abrioux alexandre-abrioux left a comment

Choose a reason for hiding this comment

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

I think we can have the same outcome without needing a new property persist = true | false in each class.

What I have in mind is the following:

  • Turn HttpDataAccess into a CombinedDataAccess by splitting it into 2 classes: HttpDataRead and HttpDataWrite
  • Export a new writer class in the data-access package: NoPersistDataWrite
  • Depending on if the user wants to persist data or not, they can use the HttpDataWrite or the NoPersistDataWrite
  • Builders that have their own custom CombinedDataAccess can also use the new NoPersistDataWrite if they need to
                                                
                                                
              ┌────────────────┐                
              │ HttpDataAccess │                
              └────────┬───────┘                
                       │                        
                       │                        
            ┌──────────▼─────────┐              
            │ CombinedDataAccess │              
            └┬────────────────┬──┘              
             │                │                 
             │                │                 
  ┌──────────▼─────┐     ┌────▼───────────┐     
  │ HttpDataRead   │     │ HttpDataWrite  │     
  └────────────────┘     └────────────────┘     
                                 or             
                         ┌────────────────────┐ 
                         │ NoPersistDataWrite │ 
                         └────────────────────┘ 
                                                
                                                
                                                
                                                

packages/data-access/src/combined-data-access.ts Outdated Show resolved Hide resolved
@MantisClone
Copy link
Member

@kevindavee How urgently do you need this Pull Request merged?

@aimensahnoun please review, given that you are most familiar with the original feature.

@MantisClone
Copy link
Member

I think we can have the same outcome without needing a new property persist = true | false in each class.

What I have in mind is the following:

  • Turn HttpDataAccess into a CombinedDataAccess by splitting it into 2 classes: HttpDataRead and HttpDataWrite
  • Export a new writer class in the data-access package: NoPersistDataWrite
  • Depending on if the user wants to persist data or not, they can use the HttpDataWrite or the NoPersistDataWrite
  • Builders that have their own custom CombinedDataAccess can also use the new NoPersistDataWrite if they need to
                                                
                                                
              ┌────────────────┐                
              │ HttpDataAccess │                
              └────────┬───────┘                
                       │                        
                       │                        
            ┌──────────▼─────────┐              
            │ CombinedDataAccess │              
            └┬────────────────┬──┘              
             │                │                 
             │                │                 
  ┌──────────▼─────┐     ┌────▼───────────┐     
  │ HttpDataRead   │     │ HttpDataWrite  │     
  └────────────────┘     └────────────────┘     
                                 or             
                         ┌────────────────────┐ 
                         │ NoPersistDataWrite │ 
                         └────────────────────┘ 
                                                
                                                
                                                
                                                

I agree with this approach, despite this being a breaking change to the NoPersistHttpDataAccess which I had considered part of the public interface.

I don't think we need to maintain backward compatibility as this InMemory Request feature is still rather new. I'll make sure that our other app builders are informed of the breaking change and can migrate if needed.

@kevindavee
Copy link
Contributor Author

@alexandre-abrioux , thanks for the suggestion. I think your approach is cleaner.

@MantisClone , not that urgent. This change is needed for better performance in Request Finance. But nothing is blocking us right now.

I agree with this approach, despite this being a breaking change to the NoPersistHttpDataAccess which I had considered part of the public interface.

NoPersistHttpDataAccess isn't used publicly. We need to use it through skipPersistence flag, right? We still have skipPersistence flag.

@kevindavee
Copy link
Contributor Author

@alexandre-abrioux, I've tried to implement your approach. It could manage the dependency injection for a persistent or non-persistent DataWrite class. However, we have several places that check whether the class persists the request.

Examples:

We can't rely on the class type since we don't know what class type is used during run-time. We still need a property that we can read during run-time. Even though we can check HttpDataAccess.writer instanceof NoPersistDataWrite, we can't do that inside RequestNetwork since we depend on the IDataAccess interface.

private dataAccess: DataAccessTypes.IDataAccess;

@alexandre-abrioux
Copy link
Member

alexandre-abrioux commented Jan 8, 2025

@kevindavee What about keeping the NoPersistHttpDataAccess and doing it like this:

             ┌────────────────┐                
             │ HttpDataAccess │                
             └────────┬───────┘                
                      │                        
           ┌──────────▼─────────┐              
           │ CombinedDataAccess │              
           └┬────────────────┬──┘              
            │                │                 
 ┌──────────▼─────┐     ┌────▼───────────┐     
 │ HttpDataRead   │     │ HttpDataWrite  │     
 └────────────────┘     └────────────────┘     
                                               
                                               
                                               
                                               
         ┌─────────────────────────┐           
         │ NoPersistHttpDataAccess │           
         └─────────────┬───────────┘           
                       │                       
            ┌──────────▼─────────┐             
            │ CombinedDataAccess │             
            └┬────────────────┬──┘             
             │                │                
  ┌──────────▼─────┐     ┌────▼──────────────┐ 
  │ HttpDataRead   │     │ NoPersitDataWrite │ 
  └────────────────┘     └───────────────────┘ 
                                               

@kevindavee
Copy link
Contributor Author

For dependency injection, that works. But how do we know from IDataAccess interface if a data writer is persisting the request or no? @alexandre-abrioux

@alexandre-abrioux
Copy link
Member

alexandre-abrioux commented Jan 8, 2025

@kevindavee By keeping the original code, since in this second solution, we don't remove NoPersistHttpDataAccess.
The original code uses if (this.dataAccess instanceof NoPersistHttpDataAccess)

@alexandre-abrioux
Copy link
Member

Never mind my answer above; you're right. It does not account for builders using customer DataAccess implementations.

I think it could work if you make the reader and writer properties of CombinedDataAccess public (+ read-only) and use:

if (this.dataAccess instanceof CombinedDataAccess && this.dataAccess.writer instanceof NoPersistDataWrite) {
  ...
}

@kevindavee
Copy link
Contributor Author

I think we're almost there.

CombinedDataAccess is an abstract class, and most of the time, we depend on IDataAccess; hence, there should be an interface change for IDataAccess or IDataWrite (like what I did).

Maybe instead of using boolean, we can keep your approach with the dependency injection, but we could expose a method to know if it's persisting the request or not.

interface IDataAccess {
   isPersisting(): boolean;   
}

abstract class CombinedDataAccess implements DataAccessTypes.IDataAccess  {
  public isPersisting() {
     return this.writer instanceof NoPersistDataWrite
  }
}

class CustomDataAccess extends CombinedDataAccess {
   constructor() {
      super(
         new MyCustomDataRead(),
         new MyCustomDataWrite(),
      )
   }
}

class NoPersistCustomDataAccess extends CombinedDataAccess {
   constructor() {
      super(
         new MyCustomDataRead(),
         new NoPersistDataWrite(), // imported from @request-network/data-access
      )
   }
}

class MyCustomDataAccessWhichIsNotUsingCombinedDataAccess {
  public isPersisting() {
     // handle it's own logic
  }
}

@alexandre-abrioux

@alexandre-abrioux
Copy link
Member

@kevindavee Looks good 🙂

@kevindavee kevindavee force-pushed the feat/allow-no-persist-for-custom-data-access branch from 970bba8 to dcf76d9 Compare January 10, 2025 04:13
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: 11

🧹 Nitpick comments (8)
packages/data-access/src/no-persist-data-write.ts (1)

25-27: Consider using more precise timestamp.

Using Date.now()/1000 might lose precision. Consider using a more precise method or storing the full millisecond timestamp.

-          timestamp: Date.now() / 1000,
+          timestamp: Math.floor(Date.now() / 1000), // Ensure consistent integer timestamps
packages/request-client.js/src/http-transaction.ts (1)

17-30: Document retry behavior in method JSDoc.

The method uses retry logic with configurable parameters, but this behavior isn't documented in the method's JSDoc.

 /**
  * Gets a transaction from the node through HTTP.
  * @param transactionData The transaction data
+ * @throws {Error} If hash normalization fails
+ * @remarks This method implements exponential backoff retry logic with the following parameters:
+ * - Maximum retries: Configured via httpConfig.getConfirmationMaxRetry
+ * - Initial retry delay: Configured via httpConfig.getConfirmationRetryDelay
+ * - Exponential backoff: Configured via httpConfig.getConfirmationExponentialBackoffDelay
+ * - Maximum backoff delay: Configured via httpConfig.getConfirmationMaxExponentialBackoffDelay
  */
packages/request-client.js/src/http-data-read.ts (1)

61-66: Extract pagination parameter handling to a utility function.

The pagination parameter handling logic is duplicated across methods. Consider extracting it to a reusable utility function.

+private buildPaginationParams(page?: number, pageSize?: number) {
+  return {
+    ...(page !== undefined && { page }),
+    ...(pageSize !== undefined && { pageSize }),
+  };
+}

 const params = {
   topic,
   updatedBetween,
-  ...(page !== undefined && { page }),
-  ...(pageSize !== undefined && { pageSize }),
+  ...this.buildPaginationParams(page, pageSize),
 };
packages/request-client.js/src/http-data-write.ts (1)

48-53: Add request timeout configuration.

The fetch request doesn't have a timeout configuration, which could lead to hanging requests in case of network issues.

     const data = await this.dataAccessConfig.fetch<DataAccessTypes.IReturnPersistTransactionRaw>(
       'POST',
       '/persistTransaction',
-      undefined,
+      {
+        timeout: this.dataAccessConfig.httpConfig.requestTimeout || 30000, // Default 30s timeout
+      },
       { channelId, topics, transactionData },
     );
packages/request-client.js/src/http-data-access-config.ts (1)

85-93: Consider using URLSearchParams for parameter handling

The current implementation manually processes parameters and uses the qs library. Consider using the built-in URLSearchParams for better maintainability and native browser compatibility.

-      // qs.parse doesn't handle well mixes of string and object params
-      for (const [key, value] of Object.entries(params)) {
-        if (typeof value === 'object') {
-          params[key] = JSON.stringify(value);
-        }
-      }
-      url.search = stringify(params);
+      const searchParams = new URLSearchParams();
+      for (const [key, value] of Object.entries(params)) {
+        searchParams.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value));
+      }
+      url.search = searchParams.toString();
packages/request-client.js/src/http-data-access.ts (1)

35-42: Consider extracting initialization logic to a separate method

The constructor contains complex initialization logic. Consider extracting it to a private method for better readability and testability.

  constructor(...) {
-    const dataAccessConfig = new HttpDataAccessConfig({ httpConfig, nodeConnectionConfig });
-    const transaction = new HttpTransaction(dataAccessConfig);
-    const reader = new HttpDataRead(dataAccessConfig);
-    const writer = persist
-      ? new HttpDataWrite(dataAccessConfig, transaction)
-      : new NoPersistDataWrite();
-
-    super(reader, writer);
+    const { dataAccessConfig, transaction, reader, writer } = this.initializeComponents(
+      httpConfig,
+      nodeConnectionConfig,
+      persist
+    );
+    super(reader, writer);
     this.dataAccessConfig = dataAccessConfig;
     this.transaction = transaction;
  }

+ private initializeComponents(
+   httpConfig?: Partial<ClientTypes.IHttpDataAccessConfig>,
+   nodeConnectionConfig?: Partial<NodeConnectionConfig>,
+   persist: boolean = true
+ ) {
+   const dataAccessConfig = new HttpDataAccessConfig({ httpConfig, nodeConnectionConfig });
+   const transaction = new HttpTransaction(dataAccessConfig);
+   const reader = new HttpDataRead(dataAccessConfig);
+   const writer = persist
+     ? new HttpDataWrite(dataAccessConfig, transaction)
+     : new NoPersistDataWrite();
+   return { dataAccessConfig, transaction, reader, writer };
+ }
packages/request-client.js/src/http-metamask-data-write.ts (2)

16-16: Avoid using 'any' type for storageMeta

In the cache definition, storageMeta is typed as any. Consider defining a more specific type for storageMeta to enhance type safety and maintainability.


59-64: Verify the necessity of the network name check

The condition if (!this.networkName) may always be false since this.networkName is initialized with a default value 'private' in the constructor. Consider removing this condition or adjusting the logic if necessary.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3759082 and dcf76d9.

📒 Files selected for processing (16)
  • packages/data-access/src/combined-data-access.ts (2 hunks)
  • packages/data-access/src/index.ts (1 hunks)
  • packages/data-access/src/no-persist-data-write.ts (1 hunks)
  • packages/request-client.js/src/api/request-network.ts (4 hunks)
  • packages/request-client.js/src/http-data-access-config.ts (1 hunks)
  • packages/request-client.js/src/http-data-access.ts (8 hunks)
  • packages/request-client.js/src/http-data-read.ts (1 hunks)
  • packages/request-client.js/src/http-data-write.ts (1 hunks)
  • packages/request-client.js/src/http-metamask-data-access.ts (5 hunks)
  • packages/request-client.js/src/http-metamask-data-write.ts (1 hunks)
  • packages/request-client.js/src/http-request-network.ts (2 hunks)
  • packages/request-client.js/src/http-transaction.ts (1 hunks)
  • packages/request-client.js/src/index.ts (1 hunks)
  • packages/request-client.js/test/in-memory-request.test.ts (2 hunks)
  • packages/transaction-manager/test/index.test.ts (19 hunks)
  • packages/types/src/data-access-types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/request-client.js/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/data-access/src/index.ts
  • packages/data-access/src/combined-data-access.ts
  • packages/transaction-manager/test/index.test.ts
  • packages/types/src/data-access-types.ts
  • packages/request-client.js/src/api/request-network.ts
🧰 Additional context used
📓 Learnings (1)
packages/request-client.js/test/in-memory-request.test.ts (1)
Learnt from: MantisClone
PR: RequestNetwork/requestNetwork#1386
File: packages/request-client.js/src/api/request-network.ts:25-25
Timestamp: 2024-11-12T14:52:33.204Z
Learning: The `persistTransaction()` function in the `no-persist-http-data-access.ts` file is tested in the `in-memory-request.test.ts` file, which is separate from the typical `index.test.ts` file to avoid test interference.
🪛 Biome (1.9.4)
packages/request-client.js/test/in-memory-request.test.ts

[error] 22-24: This block statement doesn't serve any purpose and can be safely removed.

Standalone block statements without any block-level declarations are redundant in JavaScript and can be removed to simplify the code.

(lint/complexity/noUselessLoneBlockStatements)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (5)
packages/request-client.js/src/http-request-network.ts (1)

60-60: LGTM! Clean refactoring of persistence logic

The changes simplify the code by removing the conditional instantiation of NoPersistHttpDataAccess and using a single HttpDataAccess class with a persist parameter. This improves maintainability and reduces code complexity.

packages/request-client.js/src/http-data-access.ts (1)

12-14: LGTM! Good use of inheritance and composition

The class now extends CombinedDataAccess and uses composition with dataAccessConfig and transaction properties, improving modularity and separation of concerns.

packages/request-client.js/test/in-memory-request.test.ts (1)

81-98: LGTM! Good test coverage for custom data access

The test case thoroughly verifies the creation of requests using custom data access implementation.

packages/request-client.js/src/http-metamask-data-access.ts (2)

1-11: LGTM! Good architectural improvement.

The transition to extend CombinedDataAccess and the introduction of composition-based imports align well with the PR's objective of enabling custom DataAccess implementations.


87-87: LGTM! Good implementation of the new architecture.

The refactored methods properly delegate to the appropriate components while maintaining error handling:

  • persistTransaction delegates to the writer component
  • getTransactionsByChannelId uses dataAccessConfig for fetching with proper retry configuration

Also applies to: 100-107

packages/request-client.js/src/http-data-access-config.ts Outdated Show resolved Hide resolved
packages/request-client.js/src/http-metamask-data-write.ts Outdated Show resolved Hide resolved
packages/request-client.js/src/http-metamask-data-write.ts Outdated Show resolved Hide resolved
packages/request-client.js/src/http-metamask-data-write.ts Outdated Show resolved Hide resolved
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

🧹 Nitpick comments (6)
packages/request-client.js/test/api/request-network.test.ts (2)

19-19: LGTM! Consider adding test cases for persistence behavior.

The addition of isPersisting mock aligns with the interface changes. However, the test coverage could be enhanced.

Consider adding test cases to verify:

  1. Behavior when isPersisting returns false
  2. Interaction between persistence state and request creation/updates
  3. Edge cases where persistence state changes during operations

Example test structure:

describe('persistence behavior', () => {
  it('should handle non-persisting data access', async () => {
    const mockNonPersistingDataAccess = {
      ...mockDataAccess,
      isPersisting: jest.fn().mockReturnValue(false),
    };
    const requestNetwork = new RequestNetwork({ dataAccess: mockNonPersistingDataAccess });
    // Test request creation/updates with non-persisting data access
  });

  it('should handle persistence state changes', async () => {
    const mockDynamicDataAccess = {
      ...mockDataAccess,
      isPersisting: jest.fn()
        .mockReturnValueOnce(true)
        .mockReturnValueOnce(false),
    };
    const requestNetwork = new RequestNetwork({ dataAccess: mockDynamicDataAccess });
    // Test behavior when persistence state changes
  });
});

Line range hint 11-21: Consider separating persistence-related tests.

Based on the project's testing patterns, persistence-related tests are typically kept separate to avoid test interference. Consider moving the persistence behavior tests to a dedicated test file, such as request-network-persistence.test.ts.

This separation would:

  1. Maintain consistency with existing test organization
  2. Prevent potential interference with other test cases
  3. Provide a clear location for all persistence-related test scenarios
packages/request-client.js/src/http-data-access.ts (1)

Line range hint 15-23: Add JSDoc documentation for the persist parameter

Consider adding documentation for the persist parameter in the constructor's JSDoc comment to explain its purpose and impact:

   /**
    * Creates an instance of HttpDataAccess.
    * @param httpConfig @see ClientTypes.IHttpDataAccessConfig for available options.
    * @param nodeConnectionConfig Configuration options to connect to the node.
+   * @param persist Whether to persist transactions. When false, implements "Pay before Persist" functionality.
    */
packages/request-client.js/src/http-data-access-config.ts (3)

22-46: Consider making the default baseURL configurable.

The default baseURL is currently hardcoded to 'http://localhost:3000'. Consider making this configurable through environment variables or configuration files to support different environments.

   this.nodeConnectionConfig = {
-    baseURL: 'http://localhost:3000',
+    baseURL: process.env.DEFAULT_BASE_URL || 'http://localhost:3000',
     headers: {

48-74: Enhance error handling in fetchAndRetry.

Consider adding error handling to provide more context about the failure and potentially log retry attempts:

   public async fetchAndRetry<T = unknown>(
     path: string,
     params: Record<string, unknown>,
     retryConfig: {
       maxRetries?: number;
       retryDelay?: number;
       exponentialBackoffDelay?: number;
       maxExponentialBackoffDelay?: number;
     } = {},
   ): Promise<T> {
     retryConfig.maxRetries = retryConfig.maxRetries ?? this.httpConfig.httpRequestMaxRetry;
     retryConfig.retryDelay = retryConfig.retryDelay ?? this.httpConfig.httpRequestRetryDelay;
     retryConfig.exponentialBackoffDelay =
       retryConfig.exponentialBackoffDelay ?? this.httpConfig.httpRequestExponentialBackoffDelay;
     retryConfig.maxExponentialBackoffDelay =
       retryConfig.maxExponentialBackoffDelay ??
       this.httpConfig.httpRequestMaxExponentialBackoffDelay;
-    return await retry(async () => await this.fetch<T>('GET', path, params), retryConfig)();
+    try {
+      return await retry(
+        async (attempt) => {
+          console.debug(`Attempt ${attempt + 1}/${retryConfig.maxRetries} for ${path}`);
+          return await this.fetch<T>('GET', path, params);
+        },
+        retryConfig
+      )();
+    } catch (error) {
+      throw Object.assign(error, {
+        message: `Failed to fetch ${path} after ${retryConfig.maxRetries} attempts: ${error.message}`,
+        path,
+        params,
+      });
+    }

106-109: Enhance error information.

Consider including more context in the error object to aid in debugging:

-    throw Object.assign(new Error(r.statusText), {
+    throw Object.assign(new Error(`${method} ${url} failed: ${r.statusText}`), {
       status: r.status,
       statusText: r.statusText,
+      url: url.toString(),
+      method,
+      params,
+      body,
     });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between dcf76d9 and 977e88c.

📒 Files selected for processing (5)
  • packages/request-client.js/src/http-data-access-config.ts (1 hunks)
  • packages/request-client.js/src/http-data-access.ts (8 hunks)
  • packages/request-client.js/src/http-metamask-data-access.ts (3 hunks)
  • packages/request-client.js/test/api/request-network.test.ts (1 hunks)
  • packages/request-client.js/test/in-memory-request.test.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/request-client.js/test/in-memory-request.test.ts
  • packages/request-client.js/src/http-metamask-data-access.ts
🧰 Additional context used
📓 Learnings (3)
packages/request-client.js/test/api/request-network.test.ts (1)
Learnt from: MantisClone
PR: RequestNetwork/requestNetwork#1386
File: packages/request-client.js/src/api/request-network.ts:25-25
Timestamp: 2024-11-12T14:52:33.204Z
Learning: The `persistTransaction()` function in the `no-persist-http-data-access.ts` file is tested in the `in-memory-request.test.ts` file, which is separate from the typical `index.test.ts` file to avoid test interference.
packages/request-client.js/src/http-data-access-config.ts (1)
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-read.ts:34-37
Timestamp: 2025-01-10T04:25:32.038Z
Learning: In PR #1541, the HttpDataRead class was created by extracting existing logic from HttpDataAccess without adding new validations or features, to maintain a focused refactoring scope.
packages/request-client.js/src/http-data-access.ts (1)
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-read.ts:34-37
Timestamp: 2025-01-10T04:25:32.038Z
Learning: In PR #1541, the HttpDataRead class was created by extracting existing logic from HttpDataAccess without adding new validations or features, to maintain a focused refactoring scope.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (6)
packages/request-client.js/src/http-data-access.ts (4)

3-14: Well-structured architectural changes!

The refactoring improves code organization by:

  • Extending CombinedDataAccess for better code reuse
  • Introducing clear separation between read and write operations
  • Properly encapsulating configuration and transaction handling

24-28: Make the persist parameter optional in type definition

The type definition makes persist required (persist: boolean), but the constructor provides a default value. This is contradictory and could cause TypeScript compilation errors.


35-46: Clean initialization with good separation of concerns!

The constructor properly initializes components in a logical order:

  1. Configuration
  2. Transaction handler
  3. Reader
  4. Conditional writer based on persistence flag

78-78: Consistent implementation of delegation pattern!

The methods properly delegate to their respective components:

  • Transaction operations to writer/reader
  • HTTP operations to dataAccessConfig
  • Maintains existing validation logic

This approach improves maintainability and follows single responsibility principle.

Also applies to: 88-88, 101-101, 116-116, 131-131, 139-139, 156-158

packages/request-client.js/src/http-data-access-config.ts (2)

1-8: LGTM! Clean imports and type definitions.

The imports are well-organized, and the NodeConnectionConfig type is properly exported with clear, typed properties.


10-20: LGTM! Well-documented class properties.

The class properties are clearly defined with descriptive JSDoc comments explaining their purposes.

@kevindavee kevindavee force-pushed the feat/allow-no-persist-for-custom-data-access branch from 977e88c to f68fbff Compare January 10, 2025 05:06
@kevindavee
Copy link
Contributor Author

@alexandre-abrioux @MantisClone , this PR is ready to review again

Copy link
Member

@alexandre-abrioux alexandre-abrioux left a comment

Choose a reason for hiding this comment

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

Very clean, well done 👌

I have one comment regarding naming of this new boolean variable. I think we can regroup the different names (isPersisting, persist, skipPersistence) into one. I would vote for skipPersistence: to me the wording is more explicit since the default is to persist, so opting-in to the feature of not persisting means setting skipPersistence to true.

packages/data-access/src/combined-data-access.ts Outdated Show resolved Hide resolved
packages/request-client.js/src/http-request-network.ts Outdated Show resolved Hide resolved
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

🧹 Nitpick comments (3)
packages/request-client.js/src/http-data-access-config.ts (1)

102-109: Enhance error handling with response body.

The error handling could be improved by including the response body in the error object for better debugging.

     if (r.ok) {
       return await r.json();
     }
 
+    const errorBody = await r.text();
     throw Object.assign(new Error(r.statusText), {
       status: r.status,
       statusText: r.statusText,
+      body: errorBody,
     });
packages/request-client.js/test/in-memory-request.test.ts (2)

14-22: Add error handling test cases for MyCustomDataAccess.

While the class implementation is good, consider adding test cases for error scenarios:

  • Invalid pendingStore initialization
  • Error during DataAccessRead operations

79-96: Add assertions for request state.

The test verifies basic request creation, but consider adding assertions for:

  • Request state after creation
  • Persistence state of the custom data access
  • Error cases when trying to modify the request
// Add these assertions
expect(request.state).toBe('created');
expect(myCustomDataAccess.skipPersistence()).toBe(true);
await expect(request.modify({})).rejects.toThrow();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 977e88c and 88fe992.

📒 Files selected for processing (9)
  • packages/data-access/src/combined-data-access.ts (2 hunks)
  • packages/request-client.js/src/api/request-network.ts (4 hunks)
  • packages/request-client.js/src/http-data-access-config.ts (1 hunks)
  • packages/request-client.js/src/http-data-access.ts (8 hunks)
  • packages/request-client.js/src/http-request-network.ts (2 hunks)
  • packages/request-client.js/test/api/request-network.test.ts (1 hunks)
  • packages/request-client.js/test/in-memory-request.test.ts (2 hunks)
  • packages/transaction-manager/test/index.test.ts (19 hunks)
  • packages/types/src/data-access-types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/request-client.js/test/api/request-network.test.ts
  • packages/types/src/data-access-types.ts
  • packages/transaction-manager/test/index.test.ts
  • packages/data-access/src/combined-data-access.ts
  • packages/request-client.js/src/api/request-network.ts
🧰 Additional context used
📓 Learnings (4)
packages/request-client.js/src/http-request-network.ts (1)
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-access-config.ts:76-110
Timestamp: 2025-01-10T05:15:26.574Z
Learning: When refactoring code into new classes in the Request Network codebase, maintain the existing logic and behavior without modifications to minimize risk and keep changes focused on the extraction process.
packages/request-client.js/src/http-data-access.ts (2)
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-access-config.ts:76-110
Timestamp: 2025-01-10T05:15:26.574Z
Learning: When refactoring code into new classes in the Request Network codebase, maintain the existing logic and behavior without modifications to minimize risk and keep changes focused on the extraction process.
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-read.ts:34-37
Timestamp: 2025-01-10T04:25:32.038Z
Learning: In PR #1541, the HttpDataRead class was created by extracting existing logic from HttpDataAccess without adding new validations or features, to maintain a focused refactoring scope.
packages/request-client.js/src/http-data-access-config.ts (2)
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-access-config.ts:76-110
Timestamp: 2025-01-10T05:15:26.574Z
Learning: When refactoring code into new classes in the Request Network codebase, maintain the existing logic and behavior without modifications to minimize risk and keep changes focused on the extraction process.
Learnt from: kevindavee
PR: RequestNetwork/requestNetwork#1541
File: packages/request-client.js/src/http-data-read.ts:34-37
Timestamp: 2025-01-10T04:25:32.038Z
Learning: In PR #1541, the HttpDataRead class was created by extracting existing logic from HttpDataAccess without adding new validations or features, to maintain a focused refactoring scope.
packages/request-client.js/test/in-memory-request.test.ts (1)
Learnt from: MantisClone
PR: RequestNetwork/requestNetwork#1386
File: packages/request-client.js/src/api/request-network.ts:25-25
Timestamp: 2024-11-12T14:52:33.204Z
Learning: The `persistTransaction()` function in the `no-persist-http-data-access.ts` file is tested in the `in-memory-request.test.ts` file, which is separate from the typical `index.test.ts` file to avoid test interference.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test
🔇 Additional comments (5)
packages/request-client.js/src/http-request-network.ts (2)

12-12: LGTM! Clean import updates.

The imports have been correctly updated to reflect the new structure, importing NodeConnectionConfig from the new location.

Also applies to: 15-15


60-60: LGTM! Clean constructor refactoring.

The constructor has been simplified to use the new HttpDataAccess with skipPersistence parameter, making the code more maintainable and easier to understand.

packages/request-client.js/src/http-data-access.ts (3)

12-14: LGTM! Clean class inheritance update.

The class now properly extends CombinedDataAccess and declares necessary properties with appropriate access modifiers.


35-46: LGTM! Well-structured initialization.

The constructor initialization is clean and follows a good pattern:

  1. Creates configuration first
  2. Initializes transaction handler
  3. Sets up reader and writer based on skipPersistence
  4. Calls super with dependencies
  5. Stores necessary references

78-78: LGTM! Consistent delegation pattern.

The methods consistently delegate to the appropriate components (reader/writer/transaction), maintaining good separation of concerns.

Also applies to: 88-88, 101-101, 116-116, 131-131

@kevindavee kevindavee force-pushed the feat/allow-no-persist-for-custom-data-access branch from 88fe992 to 7f36372 Compare January 10, 2025 14:35
@kevindavee
Copy link
Contributor Author

@MantisClone , shall I wait for your approval?

This PR is supposed to
Close #1540

Copy link
Member

@MantisClone MantisClone left a comment

Choose a reason for hiding this comment

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

Looks good! Sorry it took so long.

@kevindavee
Copy link
Contributor Author

No worries @MantisClone ! Thank you

@kevindavee kevindavee merged commit dc752d9 into master Jan 15, 2025
10 checks passed
@kevindavee kevindavee deleted the feat/allow-no-persist-for-custom-data-access branch January 15, 2025 05:15
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.

5 participants