Skip to content

Commit

Permalink
[feature] hyperledger#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 5, 2022
1 parent 84da745 commit 0ebb861
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 16 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 20 additions & 2 deletions cli/src/torii/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,29 @@ pub(crate) async fn handle_queries(
wsv: Arc<WorldStateView>,
query_judge: QueryJudgeArc,
pagination: Pagination,
sorting: Sorting,
request: VerifiedQueryRequest,
) -> Result<Scale<VersionedPaginatedQueryResult>> {
let (valid_request, filter) = request.validate(&wsv, query_judge.as_ref())?;
let original_result = valid_request.execute(&wsv)?;
let result = filter.filter(original_result);
let (total, result) = if let Value::Vec(value) = result {
let (total, result) = if let Value::Vec(mut value) = result {
if let Sorting::ByKey(ref key) = sorting {
value.sort_by_key(|value0| match value0 {
Value::Identifiable(IdentifiableBox::Asset(asset)) => match asset.value() {
AssetValue::Store(metadata) => metadata.get(key).map_or(0, |value1| {
if let Value::U128(x) = value1 {
*x
} else {
0
}
}),
_ => 0,
},
_ => 0,
});
}

(
value.len(),
Value::Vec(value.into_iter().paginate(pagination).collect()),
Expand Down Expand Up @@ -482,11 +499,12 @@ impl Torii {
))
.and(body::versioned()),
)
.or(endpoint4(
.or(endpoint5(
handle_queries,
warp::path(uri::QUERY)
.and(add_state!(self.wsv, self.query_judge))
.and(paginate())
.and(sorting())
.and(body::query()),
))
.or(endpoint2(
Expand Down
1 change: 1 addition & 0 deletions cli/src/torii/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ async fn torii_pagination() {
Arc::clone(&torii.wsv),
Arc::clone(&torii.query_judge),
pagination,
Sorting::NoSorting,
query,
)
.map(|result| {
Expand Down
2 changes: 1 addition & 1 deletion cli/src/torii/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ impl<O: Reply, E: Reply> Reply for WarpResult<O, E> {
}
}

generate_endpoints!(2, 3, 4);
generate_endpoints!(2, 3, 4, 5);
61 changes: 55 additions & 6 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ impl Client {
&self,
request: R,
pagination: Pagination,
sorting: Sorting,
filter: PredicateBox,
) -> Result<(B, QueryResponseHandler<R>)>
where
Expand All @@ -619,6 +620,7 @@ impl Client {
B: RequestBuilder,
{
let pagination: Vec<_> = pagination.into();
let sorting: Vec<_> = sorting.into();
let request = QueryRequest::new(request.into(), self.account_id.clone(), filter);
let request: VersionedSignedQueryRequest = self.sign_query(request)?.into();

Expand All @@ -628,33 +630,80 @@ impl Client {
format!("{}/{}", &self.torii_url, uri::QUERY),
)
.params(pagination)
.params(sorting)
.headers(self.headers.clone())
.body(request.encode_versioned()),
QueryResponseHandler::default(),
))
}

/// Create a request with pagination and add the filter.
/// Create a request with pagination, sorting and add the filter.
///
/// # Errors
/// Forwards from [`Self::prepare_query_request`].
pub fn request_with_pagination_and_filter<R>(
/// Fails if sending request fails
pub fn request_with_pagination_and_filter_and_sorting<R>(
&self,
request: R,
pagination: Pagination,
sorting: Sorting,
filter: PredicateBox,
) -> QueryHandlerResult<ClientQueryOutput<R>>
where
R: Query + Into<QueryBox> + Debug,
<R::Output as TryFrom<Value>>::Error: Into<eyre::Error>, // Seems redundant
{
iroha_logger::trace!(?request, %pagination, ?filter);
let (req, resp_handler) =
self.prepare_query_request::<R, DefaultRequestBuilder>(request, pagination, filter)?;
iroha_logger::trace!(?request, %pagination, ?sorting, ?filter);
let (req, resp_handler) = self.prepare_query_request::<R, DefaultRequestBuilder>(
request, pagination, sorting, filter,
)?;
let response = req.build()?.send()?;
resp_handler.handle(response)
}

/// Create a request with pagination and sorting.
///
/// # Errors
/// Fails if sending request fails
pub fn request_with_pagination_and_sorting<R>(
&self,
request: R,
pagination: Pagination,
sorting: Sorting,
) -> QueryHandlerResult<ClientQueryOutput<R>>
where
R: Query + Into<QueryBox> + Debug,
<R::Output as TryFrom<Value>>::Error: Into<eyre::Error>,
{
self.request_with_pagination_and_filter_and_sorting(
request,
pagination,
sorting,
PredicateBox::default(),
)
}

/// Create a request with pagination and add the filter.
///
/// # Errors
/// Fails if sending request fails
pub fn request_with_pagination_and_filter<R>(
&self,
request: R,
pagination: Pagination,
filter: PredicateBox,
) -> QueryHandlerResult<ClientQueryOutput<R>>
where
R: Query + Into<QueryBox> + Debug,
<R::Output as TryFrom<Value>>::Error: Into<eyre::Error>, // Seems redundant
{
self.request_with_pagination_and_filter_and_sorting(
request,
pagination,
Sorting::default(),
filter,
)
}

/// Query API entry point. Requests queries from `Iroha` peers with pagination.
///
/// Uses default blocking http-client. If you need some custom integration, look at
Expand Down
100 changes: 100 additions & 0 deletions client/tests/integration/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,103 @@ 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 sorting = Sorting::by_key(sort_by_metadata_key.clone());

let res = test_client.request_with_pagination_and_sorting(
client::asset::by_account_id(account_id.clone()),
Pagination::new(Some(1), Some(5)),
sorting.clone(),
)?;

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,
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_and_sorting(
client::asset::by_account_id(account_id),
Pagination::new(Some(6), None),
sorting,
)?;

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(())
}
2 changes: 2 additions & 0 deletions data_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub mod permissions;
pub mod predicate;
pub mod query;
pub mod role;
pub mod sorting;
pub mod transaction;
pub mod trigger;

Expand Down Expand Up @@ -977,6 +978,7 @@ pub mod prelude {
pagination::{prelude::*, Pagination},
peer::prelude::*,
role::prelude::*,
sorting::prelude::*,
trigger::prelude::*,
EnumTryAsError, HasMetadata, IdBox, Identifiable, IdentifiableBox, Parameter,
PredicateTrait, RegistrableBox, TryAsMut, TryAsRef, ValidationError, Value,
Expand Down
2 changes: 1 addition & 1 deletion data_model/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,7 @@ pub mod asset {
impl FindAllAssets {
/// Construct [`FindAllAssets`].
pub const fn new() -> Self {
FindAllAssets
Self
}
}

Expand Down
Loading

0 comments on commit 0ebb861

Please sign in to comment.