Skip to content

Commit

Permalink
Upstream Helius features (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
tahsintunan authored Dec 14, 2023
1 parent fdd406a commit ec33003
Show file tree
Hide file tree
Showing 20 changed files with 727 additions and 185 deletions.
214 changes: 167 additions & 47 deletions das_api/src/api/api_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ use digital_asset_types::{
sea_orm_active_enums::{
OwnerType, RoyaltyTargetType, SpecificationAssetClass, SpecificationVersions,
},
SearchAssetsQuery,
Cursor, PageOptions, SearchAssetsQuery,
},
dapi::{
get_asset, get_assets_by_authority, get_assets_by_creator, get_assets_by_group,
get_assets_by_owner, get_proof_for_asset, search_assets,
get_asset, get_asset_proofs, get_assets, get_assets_by_authority, get_assets_by_creator,
get_assets_by_group, get_assets_by_owner, get_proof_for_asset, search_assets,
},
rpc::{
filter::{AssetSortBy, SearchConditionType},
response::GetGroupingResponse,
},
rpc::{filter::SearchConditionType, response::GetGroupingResponse},
rpc::{OwnershipModel, RoyaltyModel},
};
use open_rpc_derive::document_rpc;
use sea_orm::{sea_query::ConditionType, ConnectionTrait, DbBackend, Statement};

use crate::validation::validate_opt_pubkey;
use crate::validation::{validate_opt_pubkey, validate_search_with_name};
use open_rpc_schema::document::OpenrpcDocument;
use {
crate::api::*,
Expand Down Expand Up @@ -46,21 +49,37 @@ impl DasApi {
})
}

fn get_cursor(&self, cursor: &Option<String>) -> Result<Cursor, DasApiError> {
match cursor {
Some(cursor_b64) => {
let cursor_vec = bs58::decode(cursor_b64)
.into_vec()
.map_err(|_| DasApiError::CursorValidationError(cursor_b64.clone()))?;
let cursor_struct = Cursor {
id: Some(cursor_vec),
};
Ok(cursor_struct)
}
None => Ok(Cursor::default()),
}
}

fn validate_pagination(
&self,
limit: &Option<u32>,
page: &Option<u32>,
before: &Option<String>,
after: &Option<String>,
) -> Result<(), DasApiError> {
if page.is_none() && before.is_none() && after.is_none() {
return Err(DasApiError::PaginationEmptyError);
}
cursor: &Option<String>,
sorting: &Option<&AssetSorting>,
) -> Result<PageOptions, DasApiError> {
let mut is_cursor_enabled = true;
let mut page_opt = PageOptions::default();

if let Some(limit) = limit {
// make config item
if *limit > 1000 {
return Err(DasApiError::PaginationError);
return Err(DasApiError::PaginationExceededError);
}
}

Expand All @@ -70,20 +89,57 @@ impl DasApi {
}

// make config item
if before.is_some() || after.is_some() {
if before.is_some() || after.is_some() || cursor.is_some() {
return Err(DasApiError::PaginationError);
}

is_cursor_enabled = false;
}

if let Some(before) = before {
if cursor.is_some() {
return Err(DasApiError::PaginationError);
}
if let Some(sort) = &sorting {
if sort.sort_by != AssetSortBy::Id {
return Err(DasApiError::PaginationSortingValidationError);
}
}
validate_pubkey(before.clone())?;
is_cursor_enabled = false;
}

if let Some(after) = after {
if cursor.is_some() {
return Err(DasApiError::PaginationError);
}
if let Some(sort) = &sorting {
if sort.sort_by != AssetSortBy::Id {
return Err(DasApiError::PaginationSortingValidationError);
}
}
validate_pubkey(after.clone())?;
is_cursor_enabled = false;
}

Ok(())
page_opt.limit = limit.map(|x| x as u64).unwrap_or(1000);
if is_cursor_enabled {
if let Some(sort) = &sorting {
if sort.sort_by != AssetSortBy::Id {
return Err(DasApiError::PaginationSortingValidationError);
}
page_opt.cursor = Some(self.get_cursor(&cursor)?);
}
} else {
page_opt.page = page.map(|x| x as u64);
page_opt.before = before
.clone()
.map(|x| bs58::decode(x).into_vec().unwrap_or_default());
page_opt.after = after
.clone()
.map(|x| bs58::decode(x).into_vec().unwrap_or_default());
}
Ok(page_opt)
}
}

Expand Down Expand Up @@ -121,14 +177,70 @@ impl ApiContract for DasApi {
.map_err(Into::into)
}

async fn get_asset_proofs(
self: &DasApi,
payload: GetAssetProofs,
) -> Result<HashMap<String, Option<AssetProof>>, DasApiError> {
let GetAssetProofs { ids } = payload;

let batch_size = ids.len();
if batch_size > 1000 {
return Err(DasApiError::BatchSizeExceededError);
}

let id_bytes = ids
.iter()
.map(|id| validate_pubkey(id.clone()).map(|id| id.to_bytes().to_vec()))
.collect::<Result<Vec<Vec<u8>>, _>>()?;

let proofs = get_asset_proofs(&self.db_connection, id_bytes).await?;

let result: HashMap<String, Option<AssetProof>> = ids
.iter()
.map(|id| (id.clone(), proofs.get(id).cloned()))
.collect();
Ok(result)
}

async fn get_asset(self: &DasApi, payload: GetAsset) -> Result<Asset, DasApiError> {
let id = validate_pubkey(payload.id.clone())?;
let id_bytes = id.to_bytes().to_vec();
get_asset(&self.db_connection, id_bytes)
let GetAsset { id, options } = payload;
let id_bytes = validate_pubkey(id.clone())?.to_bytes().to_vec();
let options = options.unwrap_or_default();
get_asset(&self.db_connection, id_bytes, &options.into())
.await
.map_err(Into::into)
}

async fn get_assets(
self: &DasApi,
payload: GetAssets,
) -> Result<Vec<Option<Asset>>, DasApiError> {
let GetAssets { ids, options } = payload;

let batch_size = ids.len();
if batch_size > 1000 {
return Err(DasApiError::BatchSizeExceededError);
}

let id_bytes = ids
.iter()
.map(|id| validate_pubkey(id.clone()).map(|id| id.to_bytes().to_vec()))
.collect::<Result<Vec<Vec<u8>>, _>>()?;

let options = options.unwrap_or_default();

let assets = get_assets(
&self.db_connection,
id_bytes,
batch_size as u64,
&options.into(),
)
.await?;

let result: Vec<Option<Asset>> = ids.iter().map(|id| assets.get(id).cloned()).collect();
Ok(result)
}

async fn get_assets_by_owner(
self: &DasApi,
payload: GetAssetsByOwner,
Expand All @@ -140,21 +252,23 @@ impl ApiContract for DasApi {
page,
before,
after,
options,
cursor,
} = payload;
let before: Option<String> = before.filter(|before| !before.is_empty());
let after: Option<String> = after.filter(|after| !after.is_empty());
let owner_address = validate_pubkey(owner_address.clone())?;
let owner_address_bytes = owner_address.to_bytes().to_vec();
let sort_by = sort_by.unwrap_or_default();
self.validate_pagination(&limit, &page, &before, &after)?;
let options = options.unwrap_or_default();
let page_options =
self.validate_pagination(&limit, &page, &before, &after, &cursor, &Some(&sort_by))?;
get_assets_by_owner(
&self.db_connection,
owner_address_bytes,
sort_by,
limit.map(|x| x as u64).unwrap_or(1000),
page.map(|x| x as u64),
before.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
after.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
&page_options,
&options,
)
.await
.map_err(Into::into)
Expand All @@ -172,20 +286,22 @@ impl ApiContract for DasApi {
page,
before,
after,
options,
cursor,
} = payload;
let before: Option<String> = before.filter(|before| !before.is_empty());
let after: Option<String> = after.filter(|after| !after.is_empty());
let sort_by = sort_by.unwrap_or_default();
self.validate_pagination(&limit, &page, &before, &after)?;
let options = options.unwrap_or_default();
let page_options =
self.validate_pagination(&limit, &page, &before, &after, &cursor, &Some(&sort_by))?;
get_assets_by_group(
&self.db_connection,
group_key,
group_value,
sort_by,
limit.map(|x| x as u64).unwrap_or(1000),
page.map(|x| x as u64),
before.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
after.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
&page_options,
&options,
)
.await
.map_err(Into::into)
Expand All @@ -203,22 +319,24 @@ impl ApiContract for DasApi {
page,
before,
after,
options,
cursor,
} = payload;
let creator_address = validate_pubkey(creator_address.clone())?;
let creator_address_bytes = creator_address.to_bytes().to_vec();

self.validate_pagination(&limit, &page, &before, &after)?;
let sort_by = sort_by.unwrap_or_default();
let page_options =
self.validate_pagination(&limit, &page, &before, &after, &cursor, &Some(&sort_by))?;
let only_verified = only_verified.unwrap_or_default();
let options = options.unwrap_or_default();
get_assets_by_creator(
&self.db_connection,
creator_address_bytes,
only_verified,
sort_by,
limit.map(|x| x as u64).unwrap_or(1000),
page.map(|x| x as u64),
before.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
after.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
&page_options,
&options,
)
.await
.map_err(Into::into)
Expand All @@ -235,20 +353,22 @@ impl ApiContract for DasApi {
page,
before,
after,
options,
cursor,
} = payload;
let sort_by = sort_by.unwrap_or_default();
let authority_address = validate_pubkey(authority_address.clone())?;
let authority_address_bytes = authority_address.to_bytes().to_vec();
let options = options.unwrap_or_default();

self.validate_pagination(&limit, &page, &before, &after)?;
let page_options =
self.validate_pagination(&limit, &page, &before, &after, &cursor, &Some(&sort_by))?;
get_assets_by_authority(
&self.db_connection,
authority_address_bytes,
sort_by,
limit.map(|x| x as u64).unwrap_or(1000),
page.map(|x| x as u64),
before.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
after.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
&page_options,
&options,
)
.await
.map_err(Into::into)
Expand Down Expand Up @@ -282,9 +402,12 @@ impl ApiContract for DasApi {
before,
after,
json_uri,
options,
cursor,
name,
} = payload;

// Deserialize search assets query
self.validate_pagination(&limit, &page, &before, &after)?;
let spec: Option<(SpecificationVersions, SpecificationAssetClass)> =
interface.map(|x| x.into());
let specification_version = spec.clone().map(|x| x.0);
Expand All @@ -294,6 +417,7 @@ impl ApiContract for DasApi {
SearchConditionType::All => ConditionType::All,
});
let owner_address = validate_opt_pubkey(&owner_address)?;
let name = validate_search_with_name(&name, &owner_address)?;
let creator_address = validate_opt_pubkey(&creator_address)?;
let delegate = validate_opt_pubkey(&delegate)?;

Expand Down Expand Up @@ -332,20 +456,16 @@ impl ApiContract for DasApi {
royalty_amount,
burnt,
json_uri,
name,
};
let options = options.unwrap_or_default();
let sort_by = sort_by.unwrap_or_default();
let page_options =
self.validate_pagination(&limit, &page, &before, &after, &cursor, &Some(&sort_by))?;
// Execute query
search_assets(
&self.db_connection,
saq,
sort_by,
limit.map(|x| x as u64).unwrap_or(1000),
page.map(|x| x as u64),
before.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
after.map(|x| bs58::decode(x).into_vec().unwrap_or_default()),
)
.await
.map_err(Into::into)
search_assets(&self.db_connection, saq, sort_by, &page_options, &options)
.await
.map_err(Into::into)
}

async fn get_grouping(
Expand Down
Loading

0 comments on commit ec33003

Please sign in to comment.