use holo_hash::*;
use holochain_sqlite::rusqlite::*;
use holochain_zome_types::*;
use std::fmt::Debug;
use crate::prelude::HeadInfo;
use super::Params;
use super::*;
#[derive(Debug, Clone)]
pub struct ChainHeadQuery(Arc<AgentPubKey>);
impl ChainHeadQuery {
pub fn new(agent: Arc<AgentPubKey>) -> Self {
Self(agent)
}
}
impl Query for ChainHeadQuery {
type Item = Judged<SignedActionHashed>;
type State = Option<SignedActionHashed>;
type Output = Option<HeadInfo>;
fn query(&self) -> String {
"
SELECT blob, hash FROM (
SELECT Action.blob, Action.hash, MAX(action.seq)
FROM Action
JOIN DhtOp ON DhtOp.action_hash = Action.hash
WHERE Action.author = :author
) WHERE hash IS NOT NULL
"
.into()
}
fn params(&self) -> Vec<Params> {
let params = named_params! {
":author": self.0,
};
params.to_vec()
}
fn init_fold(&self) -> StateQueryResult<Self::State> {
Ok(None)
}
fn as_filter(&self) -> Box<dyn Fn(&QueryData<Self>) -> bool> {
let author = self.0.clone();
let f = move |action: &SignedActionHashed| *action.action().author() == *author;
Box::new(f)
}
fn fold(&self, state: Self::State, sh: Self::Item) -> StateQueryResult<Self::State> {
let sh = sh.data;
Ok(Some(match state {
None => sh,
Some(old) => {
if sh.action().action_seq() > old.action().action_seq() {
sh
} else {
old
}
}
}))
}
fn render<S>(&self, state: Self::State, _stores: S) -> StateQueryResult<Self::Output>
where
S: Store,
{
Ok(state.map(|sh| {
let seq = sh.action().action_seq();
let timestamp = sh.action().timestamp();
let action = sh.hashed.hash;
HeadInfo {
action,
seq,
timestamp,
}
}))
}
fn as_map(&self) -> Arc<dyn Fn(&Row) -> StateQueryResult<Self::Item>> {
let f = row_blob_and_hash_to_action("blob", "hash");
Arc::new(move |r| Ok(Judged::valid(f(r)?)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mutations::{insert_action, insert_op_lite};
use ::fixt::prelude::*;
use holochain_sqlite::schema::SCHEMA_CELL;
use holochain_types::dht_op::DhtOpLight;
use holochain_types::dht_op::OpOrder;
#[test]
fn test_chain_head_query() {
holochain_trace::test_run().ok();
let mut conn = Connection::open_in_memory().unwrap();
SCHEMA_CELL.initialize(&mut conn, None).unwrap();
let mut txn = conn
.transaction_with_behavior(TransactionBehavior::Exclusive)
.unwrap();
let author = fixt!(AgentPubKey);
let shhs: Vec<_> = vec![
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
]
.into_iter()
.enumerate()
.flat_map(|(seq, random_action)| {
let mut chain_action = random_action.clone();
chain_action.action_seq = seq as u32;
chain_action.author = author.clone();
vec![chain_action, random_action]
})
.map(|b| {
SignedActionHashed::with_presigned(
ActionHashed::from_content_sync(InitZomesComplete::from_builder(b).into()),
fixt!(Signature),
)
})
.collect();
let expected_head = shhs[8].clone();
for shh in &shhs[..6] {
let hash = shh.action_address();
let op = DhtOpLight::StoreRecord(hash.clone(), None, hash.clone().into());
let op_order = OpOrder::new(op.get_type(), shh.action().timestamp());
insert_action(&mut txn, shh).unwrap();
insert_op_lite(
&mut txn,
&op,
&fixt!(DhtOpHash),
&op_order,
&shh.action().timestamp(),
)
.unwrap();
}
let mut scratch = Scratch::new();
for shh in &shhs[6..] {
scratch.add_action(shh.clone(), ChainTopOrdering::default());
}
let query = ChainHeadQuery::new(Arc::new(author));
let head = query.run(DbScratch::new(&[&mut txn], &scratch)).unwrap();
assert_eq!(
head.unwrap(),
HeadInfo {
action: expected_head.as_hash().clone(),
seq: expected_head.action().action_seq(),
timestamp: expected_head.action().timestamp()
}
);
}
}