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

fix(torii/graphql): handle empty metadata and schema change #2731

Closed
wants to merge 1 commit into from

Conversation

lambda-0x
Copy link
Contributor

@lambda-0x lambda-0x commented Nov 28, 2024

Stack:

⚠️ Part of a stack created by spr. Do not merge manually using the UI - doing so may have unexpected results.

Summary by CodeRabbit

  • New Features

    • Enhanced metadata handling in token balance and transfer queries to allow for null values, improving error management and robustness.
  • Bug Fixes

    • Adjusted processing logic to safely handle cases where metadata may be absent, preventing potential runtime errors.
  • Documentation

    • Added comments and warnings to clarify new logic related to metadata handling.

Copy link

coderabbitai bot commented Nov 28, 2024

Walkthrough

Ohayo, sensei! This pull request introduces several modifications to type mappings and struct definitions in the GraphQL implementation. Key changes include updating the nullability of fields in the PAGE_INFO_TYPE_MAPPING, ERC20_TOKEN_TYPE_MAPPING, and ERC721_TOKEN_TYPE_MAPPING. Additionally, the metadata field in both BalanceQueryResultRaw and TransferQueryResultRaw structs has been changed to allow null values, necessitating updates in related processing functions to handle potential nulls safely.

Changes

File Path Change Summary
crates/torii/graphql/src/mapping.rs Updated PAGE_INFO_TYPE_MAPPING fields hasPreviousPage and hasNextPage to non-nullable. Changed decimals in ERC20_TOKEN_TYPE_MAPPING from String to Int. Reverted metadataName, metadataDescription, and metadataAttributes in ERC721_TOKEN_TYPE_MAPPING to nullable.
crates/torii/graphql/src/object/erc/token_balance.rs Changed metadata in BalanceQueryResultRaw from String to Option<String>, updated processing logic to handle null values.
crates/torii/graphql/src/object/erc/token_transfer.rs Changed metadata in TransferQueryResultRaw from String to Option<String>, updated processing logic to handle null values.

Possibly related PRs

Suggested reviewers

  • glihm

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

Copy link

@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 (6)
crates/torii/graphql/src/object/erc/token_balance.rs (2)

237-242: Consider adding debug logs for skipped tokens, sensei

While safely handling null metadata by skipping tokens is good, it might be helpful to add debug logging when tokens are skipped. This would help with troubleshooting and monitoring the frequency of missing metadata.

 if row.metadata.is_none() {
+    tracing::debug!(
+        "Skipping token with id {} due to missing metadata",
+        row.token_id
+    );
     continue;
 }

244-244: Enhance error message for JSON parsing, sensei

The expect message could be more specific to help with debugging.

-    serde_json::from_str(metadata_str).expect("metadata is always json");
+    serde_json::from_str(metadata_str).expect("Failed to parse token metadata JSON");
crates/torii/graphql/src/object/erc/token_transfer.rs (2)

266-270: Ohayo! Consider adding observability for skipped tokens

While silently skipping tokens with null metadata is a valid approach, it would be helpful to add logging or metrics to track how often this occurs. This information could be valuable for monitoring and debugging.

 if row.metadata.is_none() {
+    warn!("Skipping token transfer for token_id {} due to null metadata", row.token_id);
     continue;
 }

286-286: Optimize metadata string handling

The to_owned() call creates an unnecessary clone since metadata_str is already a reference. We can use metadata_str.to_string() or simply dereference it.

-metadata: metadata_str.to_owned(),
+metadata: (*metadata_str).to_string(),
crates/torii/core/src/executor/erc.rs (2)

179-183: Enhance error logging for better debugging, sensei!

While the error handling is good, we could make debugging easier by including the contract address in the error message.

 error!(
-    "Failed to fetch token_uri for token_id: {}",
-    register_erc721_token.actual_token_id
+    "Failed to fetch token_uri for token_id: {} from contract: {}",
+    register_erc721_token.actual_token_id,
+    register_erc721_token.contract_address
 );

199-204: Consider extracting common error logging pattern, sensei!

There's a repeated pattern of error logging with token_id. Consider extracting this into a helper method for better maintainability.

+impl<'c, P: Provider + Sync + Send + 'static> Executor<'c, P> {
+    fn log_token_error(token_id: &U256, message: &str, error: Option<&anyhow::Error>) {
+        match error {
+            Some(e) => error!("{} for token_id: {}, error: {}", message, token_id, e),
+            None => error!("{} for token_id: {}", message, token_id),
+        }
+    }
+}

// Usage:
-error!(
-    "token_uri is neither ByteArray nor Array<Felt> for token_id: {}",
-    register_erc721_token.actual_token_id
-);
+Self::log_token_error(
+    &register_erc721_token.actual_token_id,
+    "token_uri is neither ByteArray nor Array<Felt>",
+    None
+);

-error!(
-    "Failed to fetch metadata for token_id: {}, error: {}",
-    register_erc721_token.actual_token_id, e
-);
+Self::log_token_error(
+    &register_erc721_token.actual_token_id,
+    "Failed to fetch metadata",
+    Some(&e)
+);

Also applies to: 214-219

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ebcc23d and d1c7629.

📒 Files selected for processing (4)
  • crates/torii/core/src/executor/erc.rs (4 hunks)
  • crates/torii/graphql/src/mapping.rs (3 hunks)
  • crates/torii/graphql/src/object/erc/token_balance.rs (3 hunks)
  • crates/torii/graphql/src/object/erc/token_transfer.rs (3 hunks)
🔇 Additional comments (7)
crates/torii/graphql/src/mapping.rs (3)

103-104: Ohayo! Nice improvement to pagination type safety, sensei!

Making hasPreviousPage and hasNextPage non-nullable is a good practice as these pagination flags should always have a definite value.


174-176: Ohayo! Good handling of optional metadata fields, sensei!

Making metadata fields nullable is a good change that aligns with real-world scenarios where NFT metadata might not always be available.


163-163: Ohayo! Please verify the impact of the decimals type change, sensei!

Changing decimals from STRING to INT is more semantically correct for ERC20 tokens. However, this is a breaking change that might affect clients.

Let's check for any direct usage of the decimals field:

✅ Verification successful

Let me search for more specific patterns to understand the impact better:


Ohayo! The decimals type change is safe to proceed, sensei!

The codebase consistently uses u8 for decimals across all internal representations:

  • Core types use u8 in core/src/types.rs
  • GraphQL objects use u8 in graphql/src/object/erc/erc_token.rs
  • GRPC types use u8 in grpc/src/types/mod.rs

The change from STRING to INT in the GraphQL schema actually aligns better with the internal representation, making it more type-safe. No client code was found using the string representation, suggesting this change won't cause breaking issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any direct usage of the decimals field that might be affected
rg -A 3 "decimals.*String" --type rust

Length of output: 40


Script:

#!/bin/bash
# Let's search for any usage of decimals field in a broader context
rg -i "decimals" --type rust -A 3

# Also check for any GraphQL schema definitions that might be affected
fd ".graphql$" --exec cat {}

Length of output: 13711


Script:

#!/bin/bash
# Search for type definitions and resolvers related to decimals
ast-grep --pattern 'fn decimals'

# Look for any test cases that might need updates
rg "test.*decimals" --type rust -A 3

Length of output: 73

crates/torii/graphql/src/object/erc/token_balance.rs (1)

304-304: Ohayo! Nice type safety improvement, sensei!

Making metadata an Option<String> is a great change that explicitly handles cases where token metadata might not exist.

crates/torii/graphql/src/object/erc/token_transfer.rs (1)

342-342: LGTM! Good handling of optional metadata

The change to make metadata optional (Option<String>) aligns well with the PR objective to handle cases where token_id no longer exists.

crates/torii/core/src/executor/erc.rs (2)

33-33: Ohayo sensei! Nice improvement to the metadata field type!

Making metadata optional is the right approach as NFT metadata might not always be available, especially when the token_id no longer exists.


186-238: Verify error handling strategy for critical failures

While gracefully handling failures is good, let's ensure we're not silently swallowing critical errors that might indicate larger issues (e.g., contract implementation problems).

✅ Verification successful

Let me gather more context about the error handling patterns to make a complete assessment.


Based on the analysis of the error handling patterns, I can now provide a final response:

Error handling strategy is appropriate for metadata operations

The current error handling approach is well-designed because:

  • Non-critical metadata failures (token_uri parsing, metadata fetching) are properly logged and gracefully degraded to None
  • Critical contract operations use proper error propagation with ? operator
  • All error cases are logged with appropriate context for debugging
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for patterns that might indicate critical contract issues

# Look for similar error patterns in other files
rg -A 3 "Failed to fetch (token_uri|metadata)"

# Look for contract verification or validation patterns
ast-grep --pattern 'fn $_(contract_address: $_, $$$) {
  $$$
  .call($$$)
  $$$
}'

Length of output: 1818


Script:

#!/bin/bash
# Look for error handling patterns in the ERC module
rg -A 5 "error!|warn!" crates/torii/core/src/executor/erc.rs

# Check if there's any retry logic or fallback mechanisms
ast-grep --pattern 'match $_ {
  Ok($_) => $_,
  Err($_) => {
    $$$
  }
}'

# Look for other metadata-related error handling
rg -A 3 "metadata.*Error|Error.*metadata" 

Length of output: 121818

(Name::new("metadataAttributes"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
(Name::new("metadataName"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("metadataDescription"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("metadataAttributes"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("imagePath"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Ohayo! The imagePath should indeed be nullable, sensei!

Looking at the codebase, image_path is constructed using a simple format string: format!("{}/{}", token_id.join("/"), "image"). This is a static path construction that doesn't verify the actual existence of the image, while other metadata fields (name, description, attributes) are properly wrapped in Option. For consistency and correctness, it should be nullable.

  • In crates/torii/graphql/src/object/erc/erc_token.rs, image_path is defined as a non-optional String
  • In crates/torii/graphql/src/mapping.rs, it's mapped as non-nullable TypeRef::named_nn
  • The path construction in both token_transfer.rs and token_balance.rs doesn't validate image existence
-(Name::new("imagePath"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
+(Name::new("imagePath"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
🔗 Analysis chain

Consider making imagePath nullable for consistency

While the metadata fields are now properly nullable, imagePath remains non-nullable (TypeRef::named_nn). Should this also be nullable since it's part of the metadata that might not be available?

Let's check if imagePath is always guaranteed to be present:

Consider making imagePath nullable for consistency with other metadata fields:

-(Name::new("imagePath"), TypeData::Simple(TypeRef::named_nn(TypeRef::STRING))),
+(Name::new("imagePath"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for imagePath handling to understand if it's always present
rg -A 5 "imagePath.*Some" --type rust
rg -A 5 "imagePath.*None" --type rust

Length of output: 79


Script:

#!/bin/bash
# Let's search for imagePath usage in the codebase more broadly
rg "imagePath" --type rust -B 2 -A 2

# Also check for any struct definitions or types that might contain imagePath
ast-grep --pattern 'struct $_ {
  $$$
  image_path: $_,
  $$$
}'

Length of output: 1080


Script:

#!/bin/bash
# Let's check the token struct definition to see how image_path is defined
rg "image_path" --type rust -B 5 -A 5

# Also check for any Option wrapping of image_path in struct definitions
ast-grep --pattern 'struct $_ {
  $$$
  image_path: Option<$_>,
  $$$
}'

Length of output: 6109

Comment on lines +238 to +240
if row.metadata.is_none() {
continue;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Strengthen token_id parsing robustness, sensei

The current token_id parsing using split and assert could panic if the format is unexpected. Consider using a more robust approach.

-let token_id = row.token_id.split(':').collect::<Vec<&str>>();
-assert!(token_id.len() == 2);
+let token_id: Vec<&str> = row.token_id.split(':').collect();
+if token_id.len() != 2 {
+    warn!(
+        "Skipping token due to invalid token_id format: {}",
+        row.token_id
+    );
+    continue;
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +271 to +273
let metadata_str = row.metadata.as_ref().unwrap();
let metadata: serde_json::Value =
serde_json::from_str(&row.metadata).expect("metadata is always json");
serde_json::from_str(metadata_str).expect("metadata is always json");
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling for metadata JSON parsing, sensei!

The current implementation uses expect() which could panic if the metadata JSON is malformed. Consider using serde_json::from_str().map_err() to handle parsing errors gracefully.

-let metadata_str = row.metadata.as_ref().unwrap();
-let metadata: serde_json::Value =
-    serde_json::from_str(metadata_str).expect("metadata is always json");
+let metadata_str = row.metadata.as_ref().unwrap();
+let metadata: serde_json::Value = match serde_json::from_str(metadata_str) {
+    Ok(value) => value,
+    Err(e) => {
+        warn!("Failed to parse metadata JSON for token_id {}: {}", row.token_id, e);
+        continue;
+    }
+};
📝 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
let metadata_str = row.metadata.as_ref().unwrap();
let metadata: serde_json::Value =
serde_json::from_str(&row.metadata).expect("metadata is always json");
serde_json::from_str(metadata_str).expect("metadata is always json");
let metadata_str = row.metadata.as_ref().unwrap();
let metadata: serde_json::Value = match serde_json::from_str(metadata_str) {
Ok(value) => value,
Err(e) => {
warn!("Failed to parse metadata JSON for token_id {}: {}", row.token_id, e);
continue;
}
};

Copy link

codecov bot commented Nov 28, 2024

Codecov Report

Attention: Patch coverage is 0% with 14 lines in your changes missing coverage. Please review.

Project coverage is 56.09%. Comparing base (6644564) to head (acf546b).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...ates/torii/graphql/src/object/erc/token_balance.rs 0.00% 7 Missing ⚠️
...tes/torii/graphql/src/object/erc/token_transfer.rs 0.00% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2731      +/-   ##
==========================================
- Coverage   56.10%   56.09%   -0.02%     
==========================================
  Files         427      427              
  Lines       54522    54532      +10     
==========================================
  Hits        30587    30587              
- Misses      23935    23945      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -30,7 +30,7 @@ pub struct RegisterErc721TokenMetadata {
pub query: RegisterErc721TokenQuery,
pub name: String,
pub symbol: String,
pub metadata: String,
pub metadata: Option<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the case when this actually happens?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there are two cases:

  • nft got burnt but we still need to process the transfers
  • the metadata json stored in the contract is invalid

Copy link

@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

🧹 Outside diff range and nitpick comments (1)
crates/torii/graphql/src/object/erc/token_balance.rs (1)

237-242: Add logging for skipped tokens, sensei

While the null check is good, silently skipping tokens might hide issues from users. Consider adding a warning log when skipping tokens due to null metadata.

 if row.metadata.is_none() {
+    warn!(
+        "Skipping token due to null metadata. Token ID: {}",
+        row.token_id
+    );
     continue;
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 752a88e and acf546b.

📒 Files selected for processing (3)
  • crates/torii/graphql/src/mapping.rs (3 hunks)
  • crates/torii/graphql/src/object/erc/token_balance.rs (3 hunks)
  • crates/torii/graphql/src/object/erc/token_transfer.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/torii/graphql/src/object/erc/token_transfer.rs
  • crates/torii/graphql/src/mapping.rs
🔇 Additional comments (3)
crates/torii/graphql/src/object/erc/token_balance.rs (3)

304-304: Ohayo! LGTM on the metadata field change, sensei!

The change to Option<String> properly handles cases where metadata might be null, making the code more robust.


257-257: LGTM on metadata string handling, sensei!

The use of to_owned() ensures proper ownership of the metadata string.


238-240: ⚠️ Potential issue

Strengthen token_id parsing robustness, sensei

The current token_id parsing using split and assert could panic if the format is unexpected. Consider using a more robust approach.

-let token_id = row.token_id.split(':').collect::<Vec<&str>>();
-assert!(token_id.len() == 2);
+let token_id: Vec<&str> = row.token_id.split(':').collect();
+if token_id.len() != 2 {
+    warn!(
+        "Skipping token due to invalid token_id format: {}",
+        row.token_id
+    );
+    continue;
+}

@lambda-0x lambda-0x changed the title fix(torii): handle case where token_id no longer exists and few graphql changes fix(torii/graphql): handle empty metadata Dec 3, 2024
@lambda-0x lambda-0x changed the title fix(torii/graphql): handle empty metadata fix(torii/graphql): handle empty metadata and schema change Dec 3, 2024
@lambda-0x
Copy link
Contributor Author

✓ Commit merged in pull request #2733

@lambda-0x lambda-0x closed this Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants