Skip to content

Commit

Permalink
feat!: allow a simplistic type-erased query execution in client cli
Browse files Browse the repository at this point in the history
Signed-off-by: ⭐️NINIKA⭐️ <[email protected]>
  • Loading branch information
DCNick3 committed Jul 10, 2024
1 parent ee16955 commit 84a09ad
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 36 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.

1 change: 1 addition & 0 deletions client_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ eyre = { workspace = true }
clap = { workspace = true, features = ["derive"] }
json5 = { workspace = true }
once_cell = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
erased-serde = "0.4.5"
supports-color = { workspace = true }
Expand Down
103 changes: 69 additions & 34 deletions client_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ use std::{
use erased_serde::Serialize;
use error_stack::{fmt::ColorMode, IntoReportCompat, ResultExt};
use eyre::{eyre, Error, Result, WrapErr};
use iroha::{
client::Client,
config::Config,
data_model::prelude::*,
};
use iroha::{client::Client, config::Config, data_model::prelude::*};
use iroha_primitives::{addr::SocketAddr, json::JsonString};
use thiserror::Error;

Expand Down Expand Up @@ -252,19 +248,44 @@ fn submit(
}

mod filter {
use iroha::data_model::query::predicate::PredicateBox;
use iroha::data_model::query::predicate::{
predicate_atoms::{
account::AccountPredicateBox, asset::AssetPredicateBox, domain::DomainPredicateBox,
},
CompoundPredicate,
};
use serde::Deserialize;

use super::*;

/// Filter for queries
/// Filter for domain queries
#[derive(Clone, Debug, clap::Parser)]
pub struct DomainFilter {
/// Predicate for filtering given as JSON5 string
#[clap(value_parser = parse_json5::<CompoundPredicate<DomainPredicateBox>>)]
pub predicate: CompoundPredicate<DomainPredicateBox>,
}

/// Filter for account queries
#[derive(Clone, Debug, clap::Parser)]
pub struct Filter {
pub struct AccountFilter {
/// Predicate for filtering given as JSON5 string
#[clap(value_parser = parse_filter)]
pub predicate: PredicateBox,
#[clap(value_parser = parse_json5::<CompoundPredicate<AccountPredicateBox>>)]
pub predicate: CompoundPredicate<AccountPredicateBox>,
}

fn parse_filter(s: &str) -> Result<PredicateBox, String> {
/// Filter for asset queries
#[derive(Clone, Debug, clap::Parser)]
pub struct AssetFilter {
/// Predicate for filtering given as JSON5 string
#[clap(value_parser = parse_json5::<CompoundPredicate<AssetPredicateBox>>)]
pub predicate: CompoundPredicate<AssetPredicateBox>,
}

fn parse_json5<T>(s: &str) -> Result<T, String>
where
T: for<'a> Deserialize<'a>,
{
json5::from_str(s).map_err(|err| format!("Failed to deserialize filter from JSON5: {err}"))
}
}
Expand Down Expand Up @@ -394,7 +415,7 @@ mod domain {
/// All domains
All,
/// Filter domains by given predicate
Filter(filter::Filter),
Filter(filter::DomainFilter),
}

impl RunArgs for List {
Expand All @@ -405,9 +426,7 @@ mod domain {

let query = match self {
List::All => query,
List::Filter(_filter) => {
todo!("Apply the new-style filter from the CLI")
}
List::Filter(filter) => query.with_raw_filter(filter.predicate),
};

let result = query.execute_all().wrap_err("Failed to get all accounts")?;
Expand Down Expand Up @@ -572,7 +591,7 @@ mod account {
/// All accounts
All,
/// Filter accounts by given predicate
Filter(filter::Filter),
Filter(filter::AccountFilter),
}

impl RunArgs for List {
Expand All @@ -583,9 +602,7 @@ mod account {

let query = match self {
List::All => query,
List::Filter(_filter) => {
todo!("Apply the new-style filter from the CLI")
}
List::Filter(filter) => query.with_raw_filter(filter.predicate),
};

let result = query.execute_all().wrap_err("Failed to get all accounts")?;
Expand Down Expand Up @@ -846,7 +863,7 @@ mod asset {
/// All assets
All,
/// Filter assets by given predicate
Filter(filter::Filter),
Filter(filter::AssetFilter),
}

impl RunArgs for List {
Expand All @@ -857,9 +874,7 @@ mod asset {

let query = match self {
List::All => query,
List::Filter(_filter) => {
todo!("Apply the new-style filter from the CLI")
}
List::Filter(filter) => query.with_raw_filter(filter.predicate),
};

let result = query.execute_all().wrap_err("Failed to get all accounts")?;
Expand Down Expand Up @@ -1051,7 +1066,7 @@ mod json {
use std::io::{BufReader, Read as _};

use clap::Subcommand;
use iroha::data_model::query::QueryBox;
use iroha::data_model::query::QueryBox2;

use super::*;

Expand Down Expand Up @@ -1083,16 +1098,36 @@ mod json {
.wrap_err("Failed to submit parsed instructions")
}
Variant::Query => {
let _client = Client::new(context.configuration().clone());
let _query: QueryBox = json5::from_str(&string_content)?;
// TODO: do the new-style query here
todo!()
// let response = client
// .request(query)
// .and_then(core::convert::identity)
// .wrap_err("Failed to query response")?;
// context.print_data(&response)?;
// Ok(())
let client = Client::new(context.configuration().clone());
let query: QueryBox2 = json5::from_str(&string_content)?;

match query {
QueryBox2::Singular(query) => {
let result =
client.query(query).wrap_err("Failed to query response")?;

context.print_data(&result)?;
}
QueryBox2::Iterable(query) => {
// we can't really do type-erased iterable queries in a nice way right now...
use iroha::data_model::query::builder::QueryExecutor;

let (mut first_batch, mut continue_cursor) =
client.start_iterable_query(query)?;

while let Some(cursor) = continue_cursor {
let (next_batch, next_continue_cursor) =
<Client as QueryExecutor>::continue_iterable_query(cursor)?;

first_batch.extend(next_batch);
continue_cursor = next_continue_cursor;
}

context.print_data(&first_batch)?;
}
}

Ok(())
}
}
}
Expand Down
42 changes: 40 additions & 2 deletions data_model/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use crate::{
permission::Permission,
role::{Role, RoleId},
seal,
seal::Sealed,
transaction::{CommittedTransaction, SignedTransaction},
trigger::TriggerId,
IdBox, Identifiable, IdentifiableBox,
Expand Down Expand Up @@ -112,7 +113,9 @@ pub trait Query: Into<QueryBox> + seal::Sealed {
}

/// A [`Query`] that either returns a single value or errors out
pub trait SingularQuery: Query {}
pub trait SingularQuery: Sealed {
type Output;
}

/// A [`Query`] that returns an iterable collection of values
pub trait IterableQuery: Query {
Expand All @@ -125,9 +128,14 @@ pub trait IterableQuery: Query {
)]
pub struct IterableQueryWithFilter<Q, P> {
pub query: Q,
#[serde(default = "predicate_default")]
pub predicate: CompoundPredicate<P>,
}

fn predicate_default<P>() -> CompoundPredicate<P> {
CompoundPredicate::PASS
}

pub type IterableQueryWithFilterFor<Q> =
IterableQueryWithFilter<Q, <<Q as IterableQuery>::Item as HasPredicateBox>::PredicateBoxType>;

Expand Down Expand Up @@ -174,6 +182,26 @@ pub enum IterableQueryOutputBatchBox {
}

impl IterableQueryOutputBatchBox {
// this is used in client cli to do type-erased iterable queries
pub fn extend(&mut self, other: IterableQueryOutputBatchBox) {
match (self, other) {
(Self::Domain(v1), Self::Domain(v2)) => v1.extend(v2),
(Self::Account(v1), Self::Account(v2)) => v1.extend(v2),
(Self::Asset(v1), Self::Asset(v2)) => v1.extend(v2),
(Self::AssetDefinition(v1), Self::AssetDefinition(v2)) => v1.extend(v2),
(Self::Role(v1), Self::Role(v2)) => v1.extend(v2),
(Self::Parameter(v1), Self::Parameter(v2)) => v1.extend(v2),
(Self::Permission(v1), Self::Permission(v2)) => v1.extend(v2),
(Self::Transaction(v1), Self::Transaction(v2)) => v1.extend(v2),
(Self::Peer(v1), Self::Peer(v2)) => v1.extend(v2),
(Self::RoleId(v1), Self::RoleId(v2)) => v1.extend(v2),
(Self::TriggerId(v1), Self::TriggerId(v2)) => v1.extend(v2),
(Self::Block(v1), Self::Block(v2)) => v1.extend(v2),
(Self::BlockHeader(v1), Self::BlockHeader(v2)) => v1.extend(v2),
_ => panic!("Cannot extend different types of IterableQueryOutputBatchBox"),
}
}

pub fn len(&self) -> usize {
match self {
Self::Domain(v) => v.len(),
Expand Down Expand Up @@ -226,6 +254,12 @@ pub enum SingularQueryOutputBox {
BlockHeader(BlockHeader),
}

impl Sealed for SingularQueryBox {}

impl SingularQuery for SingularQueryBox {
type Output = SingularQueryOutputBox;
}

#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)]
pub struct IterableQueryOutput {
pub batch: IterableQueryOutputBatchBox,
Expand All @@ -245,7 +279,9 @@ impl IterableQueryOutput {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)]
#[derive(
Debug, Clone, PartialEq, Eq, Default, Decode, Encode, Deserialize, Serialize, IntoSchema,
)]
pub struct IterableQueryParams {
pub pagination: Pagination,
pub sorting: Sorting,
Expand All @@ -256,6 +292,7 @@ pub struct IterableQueryParams {
#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)]
pub struct IterableQueryWithParams {
pub query: IterableQueryBox,
#[serde(default)]
pub params: IterableQueryParams,
}

Expand Down Expand Up @@ -627,6 +664,7 @@ macro_rules! impl_queries {
impl_queries!(@impl_query $ty => $output);

impl SingularQuery for $ty {
type Output = $output;
}

$(
Expand Down

0 comments on commit 84a09ad

Please sign in to comment.