From 904f6a765c4f459f5cf871e879605f60ad01efee Mon Sep 17 00:00:00 2001 From: armyhaylenko Date: Mon, 27 Jan 2025 13:37:53 +0200 Subject: [PATCH] feat(api): add `showBurnt` option to `GetByMethodOptions` --- entities/src/api_req_params.rs | 3 + nft_ingester/src/api/dapi/converters.rs | 1 + nft_ingester/tests/api_tests.rs | 98 +++++++++++++++++++++++++ postgre-client/src/model.rs | 3 +- rocks-db/benches/misc_benchmark.rs | 2 +- tests/setup/src/lib.rs | 26 +++++++ tests/setup/src/rocks.rs | 22 +++++- 7 files changed, 150 insertions(+), 5 deletions(-) diff --git a/entities/src/api_req_params.rs b/entities/src/api_req_params.rs index 5c5c68ce..e90a783f 100644 --- a/entities/src/api_req_params.rs +++ b/entities/src/api_req_params.rs @@ -104,6 +104,8 @@ pub struct GetByMethodsOptions { pub show_zero_balance: bool, #[serde(default)] pub show_fungible: bool, + #[serde(default)] + pub show_burnt: Option, } impl From<&SearchAssetsOptions> for Options { @@ -591,6 +593,7 @@ impl From for GetByMethodsOptions { show_inscription: value.show_inscription, show_zero_balance: value.show_zero_balance, show_fungible: false, + show_burnt: None, } } } diff --git a/nft_ingester/src/api/dapi/converters.rs b/nft_ingester/src/api/dapi/converters.rs index 472c9a73..62f7c5ed 100644 --- a/nft_ingester/src/api/dapi/converters.rs +++ b/nft_ingester/src/api/dapi/converters.rs @@ -159,6 +159,7 @@ impl TryFrom for SearchAssetsQuery { validate_pubkey(asset_owner.owner_address).map(|k| k.to_bytes().to_vec())?, ), supply: Some(AssetSupply::Greater(0)), + burnt: asset_owner.options.show_burnt, ..Default::default() }) } diff --git a/nft_ingester/tests/api_tests.rs b/nft_ingester/tests/api_tests.rs index 03a970d7..fd7b6e07 100644 --- a/nft_ingester/tests/api_tests.rs +++ b/nft_ingester/tests/api_tests.rs @@ -1861,6 +1861,104 @@ mod tests { ); } + #[tokio::test] + #[tracing_test::traced_test] + async fn test_get_only_non_burnt_assets_by_owner() { + let cnt = 20; + let cli = Cli::default(); + let (env, generated_assets) = + setup::TestEnvironment::create_burnt(&cli, cnt, SLOT_UPDATED).await; + let api = create_api(&env, None); + let tasks = JoinSet::new(); + let mutexed_tasks = Arc::new(Mutex::new(tasks)); + + let ref_value = generated_assets.owners[8].clone(); + let payload = GetAssetsByOwner { + owner_address: ref_value.owner.value.map(|owner| owner.to_string()).unwrap_or_default(), + sort_by: None, + limit: None, + page: None, + before: None, + after: None, + cursor: None, + options: GetByMethodsOptions { + show_unverified_collections: true, + show_burnt: Some(false), + ..Default::default() + }, + }; + + let res = api.get_assets_by_owner(payload, mutexed_tasks.clone()).await.unwrap(); + let res_obj: AssetList = serde_json::from_value(res).unwrap(); + + // in the setup all assets were created as burnt + // meaning that if we do not explicitly specify show_burnt + // in the options, the response will be empty + assert_eq!(res_obj.total, 0, "total should be 0"); + assert_eq!(res_obj.items.len(), 0, "items length should be 0"); + } + + #[tokio::test] + #[tracing_test::traced_test] + async fn test_get_only_burnt_assets_by_owner() { + let cnt = 20; + let cli = Cli::default(); + let (env, generated_assets) = + setup::TestEnvironment::create_burnt(&cli, cnt, SLOT_UPDATED).await; + let api = create_api(&env, None); + let tasks = JoinSet::new(); + let mutexed_tasks = Arc::new(Mutex::new(tasks)); + + let ref_value = generated_assets.owners[8].clone(); + let payload = GetAssetsByOwner { + owner_address: ref_value.owner.value.map(|owner| owner.to_string()).unwrap_or_default(), + sort_by: None, + limit: None, + page: None, + before: None, + after: None, + cursor: None, + options: GetByMethodsOptions { + show_unverified_collections: true, + show_burnt: Some(true), + ..Default::default() + }, + }; + + let res = api.get_assets_by_owner(payload, mutexed_tasks.clone()).await.unwrap(); + let res_obj: AssetList = serde_json::from_value(res).unwrap(); + + assert_eq!(res_obj.total, 1, "total should be 1"); + assert_eq!(res_obj.items.len(), 1, "items length should be 1"); + } + + #[tokio::test] + #[tracing_test::traced_test] + async fn test_search_assets_excluding_burnt_assets() { + let cnt = 20; + let cli = Cli::default(); + let (env, _generated_assets) = + setup::TestEnvironment::create_burnt(&cli, cnt, SLOT_UPDATED).await; + let api = create_api(&env, None); + let tasks = JoinSet::new(); + let mutexed_tasks = Arc::new(Mutex::new(tasks)); + let limit = 20; + let payload = SearchAssets { + burnt: Some(false), + limit: Some(limit), + options: SearchAssetsOptions { + show_unverified_collections: true, + ..Default::default() + }, + ..Default::default() + }; + let res = api.search_assets(payload, mutexed_tasks.clone()).await.unwrap(); + assert!(res.is_object()); + let res_obj: AssetList = serde_json::from_value(res).unwrap(); + assert_eq!(res_obj.total, 0, "total should be 0"); + assert_eq!(res_obj.items.len(), 0, "assets length should be 0"); + } + #[tokio::test] #[tracing_test::traced_test] async fn test_get_assets_by_group() { diff --git a/postgre-client/src/model.rs b/postgre-client/src/model.rs index 4cf9e273..1e157f21 100644 --- a/postgre-client/src/model.rs +++ b/postgre-client/src/model.rs @@ -68,7 +68,7 @@ pub struct AssetSortedIndex { pub sorting_id: String, } -#[derive(Default)] +#[derive(Default, Debug)] pub struct SearchAssetsFilter { pub specification_version: Option, pub specification_asset_class: Option, @@ -92,6 +92,7 @@ pub struct SearchAssetsFilter { pub token_type: Option, } +#[derive(Debug)] pub enum AssetSupply { Greater(u64), Equal(u64), diff --git a/rocks-db/benches/misc_benchmark.rs b/rocks-db/benches/misc_benchmark.rs index c1d76818..0622c1f5 100644 --- a/rocks-db/benches/misc_benchmark.rs +++ b/rocks-db/benches/misc_benchmark.rs @@ -13,7 +13,7 @@ fn bincode_decode_benchmark(c: &mut Criterion) { let slot = 100; let assets = pubkeys .iter() - .map(|pk| setup::rocks::create_test_dynamic_data(*pk, slot, "solana".to_string())) + .map(|pk| setup::rocks::create_test_dynamic_data(*pk, slot, "solana".to_string(), false)) .map(|a| serialize(&a).unwrap()) .collect::>(); diff --git a/tests/setup/src/lib.rs b/tests/setup/src/lib.rs index 18b9f684..3d234d62 100644 --- a/tests/setup/src/lib.rs +++ b/tests/setup/src/lib.rs @@ -63,6 +63,32 @@ impl<'a> TestEnvironment<'a> { .await } + pub async fn create_burnt( + cli: &'a Cli, + cnt: usize, + slot: u64, + ) -> (TestEnvironment<'a>, rocks::GeneratedAssets) { + Self::create_and_setup_from_closures( + cli, + cnt, + slot, + &[ + SpecificationAssetClass::Unknown, + SpecificationAssetClass::ProgrammableNft, + SpecificationAssetClass::Nft, + SpecificationAssetClass::FungibleAsset, + SpecificationAssetClass::FungibleToken, + SpecificationAssetClass::MplCoreCollection, + SpecificationAssetClass::MplCoreAsset, + ], + RocksTestEnvironmentSetup::with_authority, + RocksTestEnvironmentSetup::test_owner, + RocksTestEnvironmentSetup::dynamic_data_burnt, + RocksTestEnvironmentSetup::collection_without_authority, + ) + .await + } + #[allow(clippy::too_many_arguments)] pub async fn create_and_setup_from_closures( cli: &'a Cli, diff --git a/tests/setup/src/rocks.rs b/tests/setup/src/rocks.rs index 7ae982e8..b026f6e6 100644 --- a/tests/setup/src/rocks.rs +++ b/tests/setup/src/rocks.rs @@ -328,7 +328,18 @@ impl RocksTestEnvironmentSetup { pub fn dynamic_data(pubkeys: &[Pubkey], slot: u64) -> Vec { pubkeys .iter() - .map(|pubkey| create_test_dynamic_data(*pubkey, slot, DEFAULT_TEST_URL.to_owned())) + .map(|pubkey| { + create_test_dynamic_data(*pubkey, slot, DEFAULT_TEST_URL.to_owned(), false) + }) + .collect() + } + + pub fn dynamic_data_burnt(pubkeys: &[Pubkey], slot: u64) -> Vec { + pubkeys + .iter() + .map(|pubkey| { + create_test_dynamic_data(*pubkey, slot, DEFAULT_TEST_URL.to_owned(), true) + }) .collect() } @@ -356,7 +367,12 @@ impl RocksTestEnvironmentSetup { pub const DEFAULT_PUBKEY_OF_ONES: Pubkey = Pubkey::new_from_array([1u8; 32]); pub const PUBKEY_OF_TWOS: Pubkey = Pubkey::new_from_array([2u8; 32]); -pub fn create_test_dynamic_data(pubkey: Pubkey, slot: u64, url: String) -> AssetDynamicDetails { +pub fn create_test_dynamic_data( + pubkey: Pubkey, + slot: u64, + url: String, + is_burnt: bool, +) -> AssetDynamicDetails { AssetDynamicDetails { pubkey, is_compressible: Updated::new(slot, None, false), @@ -364,7 +380,7 @@ pub fn create_test_dynamic_data(pubkey: Pubkey, slot: u64, url: String) -> Asset is_frozen: Updated::new(slot, None, false), supply: Some(Updated::new(slot, None, 1)), seq: None, - is_burnt: Updated::new(slot, None, false), + is_burnt: Updated::new(slot, None, is_burnt), was_decompressed: Some(Updated::new(slot, None, false)), onchain_data: None, creators: Updated::new(slot, None, vec![generate_test_creator()]),