diff --git a/das_api/src/api/api_impl.rs b/das_api/src/api/api_impl.rs index 4b55f43d8..9becd9724 100644 --- a/das_api/src/api/api_impl.rs +++ b/das_api/src/api/api_impl.rs @@ -23,6 +23,7 @@ use sea_orm::{sea_query::ConditionType, ConnectionTrait, DbBackend, Statement}; use crate::error::DasApiError; use crate::validation::{validate_opt_pubkey, validate_search_with_name}; use open_rpc_schema::document::OpenrpcDocument; +use std::collections::HashSet; use { crate::api::*, crate::config::Config, @@ -148,6 +149,11 @@ pub fn not_found(asset_id: &String) -> DbErr { DbErr::RecordNotFound(format!("Asset Proof for {} Not Found", asset_id)) } +pub fn remove_duplicates_ids(ids: Vec) -> Vec { + let mut hash_set = HashSet::new(); + ids.into_iter().filter(|id| hash_set.insert(id.clone())).collect() +} + #[document_rpc] #[async_trait] impl ApiContract for DasApi { @@ -218,6 +224,7 @@ impl ApiContract for DasApi { ) -> Result>, DasApiError> { let GetAssets { ids, options } = payload; + let ids = remove_duplicates_ids(ids); let batch_size = ids.len(); if batch_size > 1000 { return Err(DasApiError::BatchSizeExceededError); diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj new file mode 100644 index 000000000..1ae41d2d6 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng new file mode 100644 index 000000000..3e817c9dc Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk new file mode 100644 index 000000000..4908e565b Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk differ diff --git a/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X new file mode 100644 index 000000000..eee1c3953 Binary files /dev/null and b/integration_tests/tests/data/accounts/get_assets_with_multiple_same_ids/JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X differ diff --git a/integration_tests/tests/integration_tests/main.rs b/integration_tests/tests/integration_tests/main.rs index 7220ca96c..7d6e0474b 100644 --- a/integration_tests/tests/integration_tests/main.rs +++ b/integration_tests/tests/integration_tests/main.rs @@ -4,3 +4,4 @@ mod common; mod general_scenario_tests; mod mpl_core_tests; mod regular_nft_tests; +mod test_get_assets_with_multiple_same_ids; \ No newline at end of file diff --git a/integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap new file mode 100644 index 000000000..87e5c63f6 --- /dev/null +++ b/integration_tests/tests/integration_tests/snapshots/integration_tests__test_get_assets_with_multiple_same_ids__get_assets_with_multiple_same_ids.snap @@ -0,0 +1,139 @@ +--- +source: integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs +expression: response +snapshot_kind: text +--- +[ + { + "interface": "ProgrammableNFT", + "id": "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/8420.json", + "files": [], + "metadata": { + "name": "Mad Lads #8420", + "symbol": "MAD", + "token_standard": "ProgrammableNonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [ + { + "group_key": "collection", + "group_value": "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" + } + ], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.042, + "basis_points": 420, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv", + "share": 0, + "verified": true + }, + { + "address": "2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "supply": null, + "mutable": true, + "burnt": false + }, + { + "interface": "V1_NFT", + "id": "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "content": { + "$schema": "https://schema.metaplex.com/nft1.0.json", + "json_uri": "https://nftstorage.link/ipfs/bafkreidgvjw2atmkw2jzkkfi56arfrzaicrebzw5xwfkz3b67fq5gbvlre", + "files": [], + "metadata": { + "name": "TURTLES", + "symbol": "TURTLES", + "token_standard": "NonFungible" + }, + "links": {} + }, + "authorities": [ + { + "address": "J4tNLYMTegHE9nVjpRM17tf1EYwJnA9Crfn3KytRNcGv", + "scopes": [ + "full" + ] + } + ], + "compression": { + "eligible": false, + "compressed": false, + "data_hash": "", + "creator_hash": "", + "asset_hash": "", + "tree": "", + "seq": 0, + "leaf_id": 0 + }, + "grouping": [], + "royalty": { + "royalty_model": "creators", + "target": null, + "percent": 0.0, + "basis_points": 0, + "primary_sale_happened": true, + "locked": false + }, + "creators": [ + { + "address": "J4tNLYMTegHE9nVjpRM17tf1EYwJnA9Crfn3KytRNcGv", + "share": 100, + "verified": true + } + ], + "ownership": { + "frozen": false, + "delegated": false, + "delegate": null, + "ownership_model": "single", + "owner": "" + }, + "supply": { + "print_max_supply": 0, + "print_current_supply": 0, + "edition_nonce": 253 + }, + "mutable": false, + "burnt": false + } +] diff --git a/integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs b/integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs new file mode 100644 index 000000000..f15d297c5 --- /dev/null +++ b/integration_tests/tests/integration_tests/test_get_assets_with_multiple_same_ids.rs @@ -0,0 +1,42 @@ +use function_name::named; + +use das_api::api::{self, ApiContract}; + +use itertools::Itertools; + +use serial_test::serial; + +use super::common::*; + +#[tokio::test] +#[serial] +#[named] +async fn test_get_assets_with_multiple_same_ids() { + let name = trim_test_name(function_name!()); + let setup = TestSetup::new(name.clone()).await; + + let seeds: Vec = seed_accounts([ + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "DZAZ3mGuq7nCYGzUyw4MiA74ysr15EfqLpzCzX2cRVng", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "2ecGsTKbj7FecLwxTHaodZRFwza7m7LamqDG4YjczZMj" + ]); + + apply_migrations_and_delete_data(setup.db.clone()).await; + index_seed_events(&setup, seeds.iter().collect_vec()).await; + + let request = r#" + { + "ids": [ + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X", + "JEKKtnGvjiZ8GtATnMVgadHU41AuTbFkMW8oD2tdyV9X" + ] + } + "#; + + let request: api::GetAssets = serde_json::from_str(request).unwrap(); + let response = setup.das_api.get_assets(request).await.unwrap(); + insta::assert_json_snapshot!(name, response); +}