Skip to content

Commit

Permalink
[feature] hyperledger-iroha#2553: Add sorting to asset queries
Browse files Browse the repository at this point in the history
Signed-off-by: Vladimir Pesterev <pesterev@pm.me>
  • Loading branch information
pesterev committed Aug 2, 2022
1 parent b8fc0c6 commit acb0779
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 9 deletions.
17 changes: 15 additions & 2 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,10 +1093,15 @@ pub mod asset {
use super::*;

/// Get query to get all assets
pub const fn all() -> FindAllAssets {
pub fn all() -> FindAllAssets {
FindAllAssets::new()
}

/// Get query to get all assets with specified sorting parameter
pub fn all_sorted(sort_by_metadata_key: impl Into<EvaluatesTo<Name>>) -> FindAllAssets {
FindAllAssets::new().with_sort(sort_by_metadata_key)
}

/// Get query to get all asset definitions
pub const fn all_definitions() -> FindAllAssetsDefinitions {
FindAllAssetsDefinitions::new()
Expand All @@ -1114,6 +1119,14 @@ pub mod asset {
FindAssetsByAccountId::new(account_id)
}

/// Get query to get all assets by account id and sort parameter
pub fn by_account_id_sorted(
account_id: impl Into<EvaluatesTo<AccountId>>,
sort_by_metadata_key: impl Into<EvaluatesTo<Name>>,
) -> FindAssetsByAccountId {
FindAssetsByAccountId::new(account_id).with_sort(sort_by_metadata_key)
}

/// Get query to get an asset by its id
pub fn by_id(asset_id: impl Into<EvaluatesTo<<Asset as Identifiable>::Id>>) -> FindAssetById {
FindAssetById::new(asset_id)
Expand Down Expand Up @@ -1305,7 +1318,7 @@ mod tests {
for (status_code, err) in responses {
let resp = Response::builder().status(status_code).body(err.encode())?;

match sut.handle(resp) {
match sut.clone().handle(resp) {
Err(ClientQueryError::QueryError(actual)) => {
// PartialEq isn't implemented, so asserting by encoded repr
assert_eq!(actual.encode(), err.encode());
Expand Down
96 changes: 96 additions & 0 deletions client/tests/integration/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,99 @@ fn client_add_asset_with_name_length_more_than_limit_should_not_commit_transacti

Ok(())
}

#[test]
fn correct_pagination_assets_after_creating_new_one() -> Result<()> {
let (_rt, _peer, test_client) = <PeerBuilder>::new().start_with_runtime();

let sort_by_metadata_key = Name::from_str("sort")?;

let account_id = AccountId::from_str("alice@wonderland")?;

let mut assets = vec![];
let mut instructions: Vec<Instruction> = vec![];

for i in 0..10 {
let asset_definition_id = AssetDefinitionId::from_str(&format!("xor{}#wonderland", i))?;
let asset_definition = AssetDefinition::store(asset_definition_id.clone());
let mut asset_metadata = Metadata::new();
asset_metadata.insert_with_limits(
sort_by_metadata_key.clone(),
Value::U128(i),
MetadataLimits::new(10, 22),
)?;
let asset = Asset::new(
AssetId::new(asset_definition_id, account_id.clone()),
AssetValue::Store(asset_metadata),
);

assets.push(asset.clone());

let create_asset_definition = RegisterBox::new(asset_definition);
let create_asset = RegisterBox::new(asset);

instructions.push(create_asset_definition.into());
instructions.push(create_asset.into());
}

test_client.submit_all_blocking(instructions)?;

let res = test_client.request_with_pagination(
client::asset::by_account_id_sorted(account_id.clone(), sort_by_metadata_key.clone()),
Pagination::new(Some(1), Some(5)),
)?;

assert_eq!(
res.output
.iter()
.map(|asset| asset.id().definition_id.name.clone())
.collect::<Vec<_>>(),
assets
.iter()
.take(5)
.map(|asset| asset.id().definition_id.name.clone())
.collect::<Vec<_>>()
);

println!();

let new_asset_definition_id = AssetDefinitionId::from_str("xor10#wonderland")?;
let new_asset_definition = AssetDefinition::store(new_asset_definition_id.clone());
let mut new_asset_metadata = Metadata::new();
new_asset_metadata.insert_with_limits(
sort_by_metadata_key.clone(),
Value::U128(10),
MetadataLimits::new(10, 22),
)?;
let new_asset = Asset::new(
AssetId::new(new_asset_definition_id, account_id.clone()),
AssetValue::Store(new_asset_metadata),
);

let create_asset_definition = RegisterBox::new(new_asset_definition);
let create_asset = RegisterBox::new(new_asset.clone());

test_client.submit_all_blocking(vec![create_asset_definition.into(), create_asset.into()])?;

let res = test_client.request_with_pagination(
client::asset::by_account_id_sorted(account_id, sort_by_metadata_key),
Pagination::new(Some(6), None),
)?;

let mut right = assets.into_iter().skip(5).take(5).collect::<Vec<_>>();

right.push(new_asset);

assert_eq!(
res.output
.into_iter()
.map(|asset| asset.id().definition_id.name.clone())
.collect::<Vec<_>>(),
right
.into_iter()
.map(|asset| asset.id().definition_id.name.clone())
.collect::<Vec<_>>()
);

Ok(())
}
64 changes: 62 additions & 2 deletions core/src/smartcontracts/isi/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ pub mod isi {

/// Asset-related query implementations.
pub mod query {
use core::cmp::Ordering;

use eyre::{Result, WrapErr as _};

use super::*;
Expand All @@ -361,14 +363,31 @@ pub mod query {
impl ValidQuery for FindAllAssets {
#[metrics(+"find_all_assets")]
fn execute(&self, wsv: &WorldStateView) -> Result<Self::Output, Error> {
let sort_by_metadata_key = match self.sort_by_metadata_key.as_ref() {
Some(name) => Some(
name.evaluate(wsv, &Context::default())
.wrap_err("Failed to get sorting key")
.map_err(|e| Error::Evaluate(e.to_string()))?,
),
None => None,
};

iroha_logger::trace!(?sort_by_metadata_key);

let mut vec = Vec::new();

for domain in wsv.domains().iter() {
for account in domain.accounts() {
for asset in account.assets() {
vec.push(asset.clone())
}
}
}

if let Some(key) = sort_by_metadata_key {
vec.sort_by(|l, r| compare_by_metadata_key(&key, l, r));
}

Ok(vec)
}
}
Expand Down Expand Up @@ -451,8 +470,25 @@ pub mod query {
.evaluate(wsv, &Context::default())
.wrap_err("Failed to get account id")
.map_err(|e| Error::Evaluate(e.to_string()))?;
iroha_logger::trace!(%id);
wsv.account_assets(&id).map_err(Into::into)

let sort_by_metadata_key = match self.sort_by_metadata_key.as_ref() {
Some(name) => Some(
name.evaluate(wsv, &Context::default())
.wrap_err("Failed to get sorting key")
.map_err(|e| Error::Evaluate(e.to_string()))?,
),
None => None,
};

iroha_logger::trace!(%id, ?sort_by_metadata_key);

let mut vec = wsv.account_assets(&id).map_err(Error::from)?;

if let Some(key) = sort_by_metadata_key {
vec.sort_by(|l, r| compare_by_metadata_key(&key, l, r));
}

Ok(vec)
}
}

Expand Down Expand Up @@ -587,4 +623,28 @@ pub mod query {
.clone())
}
}

fn compare_by_metadata_key(key: &Name, left: &Asset, right: &Asset) -> Ordering {
let left = match left.value() {
AssetValue::Store(metadata) => metadata.get(key).unwrap_or(&Value::U128(0)),
_ => &Value::U128(0),
};

let left = match left {
Value::U128(value) => value,
_ => &0,
};

let right = match right.value() {
AssetValue::Store(metadata) => metadata.get(key).unwrap_or(&Value::U128(0)),
_ => &Value::U128(0),
};

let right = match right {
Value::U128(value) => value,
_ => &0,
};

left.cmp(right)
}
}
35 changes: 30 additions & 5 deletions data_model/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,6 @@ pub mod asset {
Debug,
Display,
Clone,
Copy,
Default,
PartialEq,
Eq,
Expand All @@ -683,7 +682,10 @@ pub mod asset {
Ord,
)]
#[display(fmt = "Find all assets")]
pub struct FindAllAssets;
pub struct FindAllAssets {
/// [`Name`] of the key in [`Asset`]'s metadata used to order query result.
pub sort_by_metadata_key: Option<EvaluatesTo<Name>>,
}

impl Query for FindAllAssets {
type Output = Vec<Asset>;
Expand Down Expand Up @@ -810,6 +812,8 @@ pub mod asset {
pub struct FindAssetsByAccountId {
/// [`AccountId`] under which assets should be found.
pub account_id: EvaluatesTo<AccountId>,
/// [`Name`] of the key in [`Asset`]'s metadata used to order query result.
pub sort_by_metadata_key: Option<EvaluatesTo<Name>>,
}

impl Query for FindAssetsByAccountId {
Expand Down Expand Up @@ -989,8 +993,18 @@ pub mod asset {

impl FindAllAssets {
/// Construct [`FindAllAssets`].
pub const fn new() -> Self {
FindAllAssets
pub fn new() -> Self {
Self {
sort_by_metadata_key: None,
}
}

/// Construct [`FindAllAssets`] with sort by key.
#[must_use]
pub fn with_sort(mut self, sort_by_metadata_key: impl Into<EvaluatesTo<Name>>) -> Self {
self.sort_by_metadata_key
.replace(sort_by_metadata_key.into());
self
}
}

Expand Down Expand Up @@ -1029,7 +1043,18 @@ pub mod asset {
/// Construct [`FindAssetsByAccountId`].
pub fn new(account_id: impl Into<EvaluatesTo<AccountId>>) -> Self {
let account_id = account_id.into();
FindAssetsByAccountId { account_id }
FindAssetsByAccountId {
account_id,
sort_by_metadata_key: None,
}
}

/// Construct [`FindAssetsByAccountId`] with sort by key.
#[must_use]
pub fn with_sort(mut self, sort_by_metadata_key: impl Into<EvaluatesTo<Name>>) -> Self {
self.sort_by_metadata_key
.replace(sort_by_metadata_key.into());
self
}
}

Expand Down
1 change: 1 addition & 0 deletions tools/parity_scale_decoder/src/generate_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ pub fn generate_map() -> DumpDecodedMap {
Option<events::pipeline::EntityKind>,
Option<events::pipeline::StatusKind>,
Option<events::time::Interval>,
Option<expression::EvaluatesTo<iroha_data_model::name::Name>>,
Option<isi::Instruction>,
Option<sumeragi::network_topology::Topology>,
Option<u32>,
Expand Down

0 comments on commit acb0779

Please sign in to comment.