Skip to content

Commit

Permalink
feat!: Do not allow to specify iterable-only options on non-iterable …
Browse files Browse the repository at this point in the history
…queries

This only changes the interfaces (client & smart contract) to be less error-prone, doing so is already a runtime error

Signed-off-by: Nikita Strygin <[email protected]>
  • Loading branch information
DCNick3 committed May 22, 2024
1 parent ab2431e commit 03a3b50
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 59 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ tracing-subscriber = { workspace = true, features = ["fmt", "ansi"] }
tracing-flame = "0.2.0"
once_cell = { workspace = true }

trybuild = { workspace = true }

[build-dependencies]
eyre = { workspace = true }
iroha_wasm_builder = { workspace = true }
Expand Down
29 changes: 18 additions & 11 deletions client/src/query_builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Debug;

use iroha_data_model::query::QueryOutputBox;
use iroha_data_model::query::{IterableQuery, QueryOutputBox};

use crate::{
client::{Client, QueryOutput, QueryResult},
Expand Down Expand Up @@ -33,6 +33,23 @@ where
}
}

pub fn execute(self) -> QueryResult<<R::Output as QueryOutput>::Target> {
self.client.request_with_filter_and_pagination_and_sorting(
self.request,
self.pagination,
self.fetch_size,
self.sorting,
self.filter,
)
}
}

impl<R> QueryRequestBuilder<'_, R>
where
R: IterableQuery + Debug,
R::Output: QueryOutput,
<R::Output as TryFrom<QueryOutputBox>>::Error: Into<eyre::Error>,
{
pub fn with_filter(mut self, filter: PredicateBox) -> Self {
self.filter = filter;
self
Expand All @@ -52,14 +69,4 @@ where
self.fetch_size = fetch_size;
self
}

pub fn execute(self) -> QueryResult<<R::Output as QueryOutput>::Target> {
self.client.request_with_filter_and_pagination_and_sorting(
self.request,
self.pagination,
self.fetch_size,
self.sorting,
self.filter,
)
}
}
1 change: 1 addition & 0 deletions client/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod integration;
mod ui;
8 changes: 8 additions & 0 deletions client/tests/ui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![cfg(not(coverage))]
use trybuild::TestCases;

#[test]
fn ui() {
let test_cases = TestCases::new();
test_cases.compile_fail("tests/ui_fail/*.rs");
}
19 changes: 19 additions & 0 deletions client/tests/ui_fail/cant_filter_singular_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use iroha_client::{
client::{self, Client},
config::Config,
data_model::query::predicate::{string, value, PredicateBox},
};

fn main() {
let config = Config::load("../configs/swarm/client.toml").unwrap();

let client = Client::new(config);

let result = client
.build_query(client::permission::permission_schema())
.with_filter(PredicateBox::new(
value::QueryOutputPredicate::Identifiable(string::StringPredicate::starts_with("xor_")),
))
.execute()
.unwrap();
}
24 changes: 24 additions & 0 deletions client/tests/ui_fail/cant_filter_singular_query.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0599]: the method `with_filter` exists for struct `QueryRequestBuilder<'_, FindPermissionSchema>`, but its trait bounds were not satisfied
--> tests/ui_fail/cant_filter_singular_query.rs:14:10
|
12 | let result = client
| __________________-
13 | | .build_query(client::permission::permission_schema())
14 | | .with_filter(PredicateBox::new(
| | -^^^^^^^^^^^ method cannot be called on `QueryRequestBuilder<'_, FindPermissionSchema>` due to unsatisfied trait bounds
| |_________|
|
|
::: $WORKSPACE/data_model/src/query/mod.rs
|
| / queries! {
| | /// Finds all registered permission tokens
| | #[derive(Copy, Display)]
| | #[ffi_type]
... |
| | }
| | }
| |_____- doesn't satisfy `_: IterableQuery`
|
= note: the following trait bounds were not satisfied:
`iroha_client::iroha_data_model::prelude::FindPermissionSchema: IterableQuery`
121 changes: 73 additions & 48 deletions smart_contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use data_model::{
};
use derive_more::Display;
pub use iroha_data_model as data_model;
use iroha_data_model::query::IterableQuery;
pub use iroha_smart_contract_derive::main;
pub use iroha_smart_contract_utils::{debug, error, info, log, warn};
use iroha_smart_contract_utils::{
Expand Down Expand Up @@ -180,6 +181,20 @@ pub trait ExecuteQueryOnHost: Sized {
/// Query output type.
type Output;

/// Execute query on the host
///
/// # Errors
///
/// - If query validation failed
/// - If query execution failed
fn execute(&self) -> Result<QueryOutputCursor<Self::Output>, ValidationFail>;
}

/// Extension of [`ExecuteQueryOnHost`] for iterable queries
pub trait ExecuteIterableQueryOnHost: Sized {
/// Type of the iterable query output item
type Item;

/// Apply filter to a query
fn filter(&self, predicate: impl Into<PredicateBox>) -> SmartContractQuery<Self>;
/// Apply sorting to a query
Expand All @@ -190,14 +205,6 @@ pub trait ExecuteQueryOnHost: Sized {

/// Set fetch size for a query. Default is [`DEFAULT_FETCH_SIZE`]
fn fetch_size(&self, fetch_size: FetchSize) -> SmartContractQuery<Self>;

/// Execute query on the host
///
/// # Errors
///
/// - If query validation failed
/// - If query execution failed
fn execute(&self) -> Result<QueryOutputCursor<Self::Output>, ValidationFail>;
}

impl<Q> ExecuteQueryOnHost for Q
Expand All @@ -208,6 +215,26 @@ where
{
type Output = Q::Output;

fn execute(&self) -> Result<QueryOutputCursor<Self::Output>, ValidationFail> {
SmartContractQuery {
query: self,
filter: PredicateBox::default(),
sorting: Sorting::default(),
pagination: Pagination::default(),
fetch_size: FetchSize::default(),
}
.execute()
}
}

impl<Q> ExecuteIterableQueryOnHost for Q
where
Q: IterableQuery + Encode,
Q::Output: DecodeAll,
<Q::Output as TryFrom<QueryOutputBox>>::Error: core::fmt::Debug,
{
type Item = Q::Item;

#[must_use]
fn filter(&self, predicate: impl Into<PredicateBox>) -> SmartContractQuery<Self> {
SmartContractQuery {
Expand Down Expand Up @@ -251,17 +278,6 @@ where
fetch_size,
}
}

fn execute(&self) -> Result<QueryOutputCursor<Self::Output>, ValidationFail> {
SmartContractQuery {
query: self,
filter: PredicateBox::default(),
sorting: Sorting::default(),
pagination: Pagination::default(),
fetch_size: FetchSize::default(),
}
.execute()
}
}

impl<Q> SmartContractQuery<'_, Q>
Expand All @@ -270,34 +286,6 @@ where
Q::Output: DecodeAll,
<Q::Output as TryFrom<QueryOutputBox>>::Error: core::fmt::Debug,
{
/// Apply filter to a query
#[must_use]
pub fn filter(mut self, predicate: impl Into<PredicateBox>) -> Self {
self.filter = predicate.into();
self
}

/// Apply sorting to a query
#[must_use]
pub fn sort(mut self, sorting: Sorting) -> Self {
self.sorting = sorting;
self
}

/// Apply pagination to a query
#[must_use]
pub fn paginate(mut self, pagination: Pagination) -> Self {
self.pagination = pagination;
self
}

/// Set fetch size for a query. Default is [`DEFAULT_FETCH_SIZE`]
#[must_use]
pub fn fetch_size(mut self, fetch_size: FetchSize) -> Self {
self.fetch_size = fetch_size;
self
}

/// Execute query on the host
///
/// # Errors
Expand Down Expand Up @@ -328,6 +316,41 @@ where
}
}

impl<Q> SmartContractQuery<'_, Q>
where
Q: IterableQuery + Encode,
Q::Output: DecodeAll,
<Q::Output as TryFrom<QueryOutputBox>>::Error: core::fmt::Debug,
{
/// Apply filter to a query
#[must_use]
pub fn filter(mut self, predicate: impl Into<PredicateBox>) -> Self {
self.filter = predicate.into();
self
}

/// Apply sorting to a query
#[must_use]
pub fn sort(mut self, sorting: Sorting) -> Self {
self.sorting = sorting;
self
}

/// Apply pagination to a query
#[must_use]
pub fn paginate(mut self, pagination: Pagination) -> Self {
self.pagination = pagination;
self
}

/// Set fetch size for a query. Default is [`DEFAULT_FETCH_SIZE`]
#[must_use]
pub fn fetch_size(mut self, fetch_size: FetchSize) -> Self {
self.fetch_size = fetch_size;
self
}
}

/// Cursor over query results implementing [`IntoIterator`].
///
/// If you execute [`QueryBox`] when you probably want to use [`collect()`](Self::collect) method
Expand Down Expand Up @@ -488,7 +511,9 @@ pub mod prelude {
pub use iroha_smart_contract_derive::main;
pub use iroha_smart_contract_utils::debug::DebugUnwrapExt;

pub use crate::{data_model::prelude::*, ExecuteOnHost, ExecuteQueryOnHost};
pub use crate::{
data_model::prelude::*, ExecuteIterableQueryOnHost, ExecuteOnHost, ExecuteQueryOnHost,
};
}

#[cfg(test)]
Expand Down

0 comments on commit 03a3b50

Please sign in to comment.