Skip to content

Commit

Permalink
Optionally display collection metadata of groups
Browse files Browse the repository at this point in the history
When caller includes display option of show_collection_metadata asset
data info will be included in the group area of the response.
  • Loading branch information
AhzamAkhtar authored and kespinola committed Jan 8, 2025
1 parent 313cb5f commit 6a2a2c1
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 23 deletions.
13 changes: 12 additions & 1 deletion digital_asset_types/src/dao/extensions/asset_grouping.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use sea_orm::{EntityTrait, EnumIter, Related, RelationDef, RelationTrait};

use crate::dao::{asset, asset_authority, asset_grouping};
use crate::dao::{asset, asset_authority, asset_data, asset_grouping};

#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Asset,
AssetAuthority,
AssetData,
}

impl RelationTrait for Relation {
Expand All @@ -19,6 +20,10 @@ impl RelationTrait for Relation {
.from(asset_grouping::Column::AssetId)
.to(asset_authority::Column::Id)
.into(),
Self::AssetData => asset_grouping::Entity::belongs_to(asset_data::Entity)
.from(asset_grouping::Column::AssetId)
.to(asset_data::Column::Id)
.into(),
}
}
}
Expand All @@ -34,3 +39,9 @@ impl Related<asset_authority::Entity> for asset_grouping::Entity {
Relation::AssetAuthority.def()
}
}

impl Related<asset_data::Entity> for asset_grouping::Entity {
fn to() -> RelationDef {
Relation::AssetData.def()
}
}
15 changes: 13 additions & 2 deletions digital_asset_types/src/dao/full_asset.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
use super::asset_v1_account_attachments;
use crate::dao::{asset, asset_authority, asset_creators, asset_data, asset_grouping};

use super::asset_v1_account_attachments;
pub struct FullAssetGroup {
pub id: i64,
pub asset_id: Vec<u8>,
pub group_key: String,
pub group_value: Option<String>,
pub seq: Option<i64>,
pub slot_updated: Option<i64>,
pub verified: bool,
pub group_info_seq: Option<i64>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct FullAsset {
pub asset: asset::Model,
pub data: Option<asset_data::Model>,
pub authorities: Vec<asset_authority::Model>,
pub creators: Vec<asset_creators::Model>,
pub groups: Vec<asset_grouping::Model>,
pub inscription: Option<asset_v1_account_attachments::Model>,
pub groups: Vec<(asset_grouping::Model, Option<asset_data::Model>)>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct AssetRelated {
pub authorities: Vec<asset_authority::Model>,
Expand Down
50 changes: 37 additions & 13 deletions digital_asset_types/src/dao/scopes/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,11 @@ pub async fn get_related_for_assets(
.add(asset_grouping::Column::Verified.is_null())
};

let grouping = asset_grouping::Entity::find()
let grouping_base_query = asset_grouping::Entity::find()
.filter(asset_grouping::Column::AssetId.is_in(ids.clone()))
.filter(asset_grouping::Column::GroupValue.is_not_null())
.filter(cond)
.order_by_asc(asset_grouping::Column::AssetId)
.all(conn)
.await?;
.order_by_asc(asset_grouping::Column::AssetId);

if options.show_inscription {
let attachments = asset_v1_account_attachments::Entity::find()
Expand All @@ -369,11 +367,24 @@ pub async fn get_related_for_assets(
}
}

for g in grouping.into_iter() {
if let Some(asset) = assets_map.get_mut(&g.asset_id) {
asset.groups.push(g);
if options.show_collection_metadata {
let combined_group_query = grouping_base_query
.find_also_related(asset_data::Entity)
.all(conn)
.await?;
for (g, a) in combined_group_query.into_iter() {
if let Some(asset) = assets_map.get_mut(&g.asset_id) {
asset.groups.push((g, a));
}
}
}
} else {
let single_group_query = grouping_base_query.all(conn).await?;
for g in single_group_query.into_iter() {
if let Some(asset) = assets_map.get_mut(&g.asset_id) {
asset.groups.push((g, None));
}
}
};

Ok(assets_map.into_iter().map(|(_, v)| v).collect())
}
Expand Down Expand Up @@ -445,7 +456,7 @@ pub async fn get_by_id(

filter_out_stale_creators(&mut creators);

let grouping: Vec<asset_grouping::Model> = asset_grouping::Entity::find()
let grouping_query = asset_grouping::Entity::find()
.filter(asset_grouping::Column::AssetId.eq(asset.id.clone()))
.filter(asset_grouping::Column::GroupValue.is_not_null())
.filter(
Expand All @@ -455,16 +466,29 @@ pub async fn get_by_id(
// Therefore if verified is null, we can assume that the group is verified.
.add(asset_grouping::Column::Verified.is_null()),
)
.order_by_asc(asset_grouping::Column::AssetId)
.all(conn)
.await?;
.order_by_asc(asset_grouping::Column::AssetId);

let groups = if options.show_collection_metadata {
grouping_query
.find_also_related(asset_data::Entity)
.all(conn)
.await?
} else {
grouping_query
.all(conn)
.await?
.into_iter()
.map(|g| (g, None))
.collect::<Vec<_>>()
};

Ok(FullAsset {
asset,
data,
authorities,
creators,
groups: grouping,
inscription,
groups,
})
}

Expand Down
39 changes: 32 additions & 7 deletions digital_asset_types/src/dapi/common/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,25 +308,50 @@ pub fn to_creators(creators: Vec<asset_creators::Model>) -> Vec<Creator> {
}

pub fn to_grouping(
groups: Vec<asset_grouping::Model>,
groups: Vec<(asset_grouping::Model, Option<asset_data::Model>)>,
options: &Options,
) -> Result<Vec<Group>, DbErr> {
let result: Vec<Group> = groups
.iter()
.filter_map(|model| {
.filter_map(|(asset_group, asset_data)| {
let verified = match options.show_unverified_collections {
// Null verified indicates legacy data, meaning it is verified.
true => Some(model.verified),
true => Some(asset_group.verified),
false => None,
};
// Filter out items where group_value is None.
model.group_value.clone().map(|group_value| Group {
group_key: model.group_key.clone(),
group_value: Some(group_value),
verified,
asset_group.group_value.clone().map(|group_value| {
let collection_metadata = asset_data.as_ref().map(|data| {
let mut metadata_selector_fn = jsonpath_lib::selector(&data.metadata);
let metadata_selector = &mut metadata_selector_fn;
let mut meta: MetadataMap = MetadataMap::new();

if let Some(name) = safe_select(metadata_selector, "$.name") {
meta.set_item("name", name.clone());
}
if let Some(symbol) = safe_select(metadata_selector, "$.symbol") {
meta.set_item("symbol", symbol.clone());
}
if let Some(image) = safe_select(metadata_selector, "$.image") {
meta.set_item("image", image.clone());
}
if let Some(external_url) = safe_select(metadata_selector, "$.external_url") {
meta.set_item("external_url", external_url.clone());
}

meta
});

Group {
group_key: asset_group.group_key.clone(),
group_value: Some(group_value),
verified,
collection_metadata,
}
})
})
.collect();

Ok(result)
}

Expand Down
2 changes: 2 additions & 0 deletions digital_asset_types/src/rpc/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ pub struct Group {
pub group_value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verified: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub collection_metadata: Option<MetadataMap>,
}

#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)]
Expand Down
2 changes: 2 additions & 0 deletions digital_asset_types/src/rpc/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub struct Options {
#[serde(default)]
pub show_unverified_collections: bool,
#[serde(default)]
pub show_collection_metadata: bool,
#[serde(default)]
pub show_zero_balance: bool,
#[serde(default)]
pub show_inscription: bool,
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions integration_tests/tests/integration_tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod general_scenario_tests;
mod mpl_core_tests;
mod nft_editions_tests;
mod regular_nft_tests;
mod show_collection_metadata_option_tests;
mod show_inscription_flag_tests;
mod test_show_zero_balance_filter;
mod token_accounts_tests;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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_asset_with_show_collection_metadata_option() {
let name = trim_test_name(function_name!());

let setup = TestSetup::new_with_options(
name.clone(),
TestSetupOptions {
network: Some(Network::Mainnet),
},
)
.await;

let seeds: Vec<SeedEvent> = seed_accounts([
"AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM",
"7fXKY9tPpvYsdbSNyesUqo27WYC6ZsBEULdtngGHqLCK",
"8Xv3SpX94HHf32Apg4TeSeS3i2p6wuXeE8FBZr168Hti",
]);

apply_migrations_and_delete_data(setup.db.clone()).await;

index_seed_events(&setup, seeds.iter().collect_vec()).await;

let request = r#"
{
"id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM",
"displayOptions" : {
"showCollectionMetadata": true
}
}
"#;

let request: api::GetAsset = serde_json::from_str(request).unwrap();

let response = setup.das_api.get_asset(request).await.unwrap();

insta::assert_json_snapshot!(name, response);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
source: integration_tests/tests/integration_tests/show_collection_metadata_option_tests.rs
expression: response
---
{
"interface": "ProgrammableNFT",
"id": "AH6wj7T8Ke5nbukjtcobjjs1CDWUcQxndtnLkKAdrSrM",
"content": {
"$schema": "https://schema.metaplex.com/nft1.0.json",
"json_uri": "https://madlads.s3.us-west-2.amazonaws.com/json/1983.json",
"files": [],
"metadata": {
"name": "Mad Lads #1983",
"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",
"collection_metadata": {}
}
],
"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": "GqPnSDXwp4JFtKS7YZ2HERgBbYLKpKVYy9TpVunzLRa9"
},
"mutable": true,
"burnt": false
}

0 comments on commit 6a2a2c1

Please sign in to comment.