1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use hdk::prelude::*;
use crate::{datetime_queries::utils::serialize_err, retrieval::utils::*, wire_record::WireRecord};
#[cfg(feature = "mock")]
use ::mockall::automock;
#[derive(Debug, PartialEq, Clone)]
pub struct GetLatestEntry {}
#[cfg_attr(feature = "mock", automock)]
impl GetLatestEntry {
/// If an entry at the `entry_hash` has multiple updates to itself, this
/// function will sort through them by timestamp in order to return the contents
/// of the latest update. It also has the special behaviour of returning the
/// ORIGINAL ActionHash, as opposed to the ActionHash of the Action that performed
/// that latest update. This is useful if you want hashes in your application
/// to act consistently, almost acting as an "id" in a centralized system.
/// It simplifies traversal of the update tree, since all updates
/// made by the client can reference the original, instead of updates reference updates
pub fn get_latest_for_entry<
T: 'static + TryFrom<SerializedBytes, Error = SerializedBytesError>,
>(
&self,
entry_hash: EntryHash,
get_options: GetOptions,
) -> ExternResult<Option<WireRecord<T>>> {
match get_details(entry_hash.clone(), get_options.clone())? {
Some(Details::Entry(details)) => match details.entry_dht_status {
EntryDhtStatus::Live => {
let first_action = details.actions.first().unwrap();
let created_at = first_action.action().timestamp();
match details.updates.len() {
// pass out the action associated with this entry
0 => {
let updated_at = created_at.clone();
let maybe_entry_and_hashes = original_action_hash_with_entry(
get_action_hash(first_action.to_owned()),
get_options,
)?;
match maybe_entry_and_hashes {
Some(entry_and_hashes) => Ok(Some(WireRecord {
action_hash: entry_and_hashes.1.into(),
entry_hash: entry_and_hashes.2.into(),
entry: entry_and_hashes.0,
created_at,
updated_at,
})),
None => Ok(None),
}
}
_ => {
let mut sortlist = details.updates.to_vec();
// unix timestamp should work for sorting
sortlist.sort_by_key(|update| update.action().timestamp().as_millis());
// sorts in ascending order, so take the last record
let last = sortlist.last().unwrap().to_owned();
let updated_at = last.action().timestamp();
let maybe_entry_and_hashes = original_action_hash_with_entry(
get_action_hash(last),
get_options,
)?;
match maybe_entry_and_hashes {
Some(entry_and_hashes) => Ok(Some(WireRecord {
action_hash: entry_and_hashes.1.into(),
entry_hash: entry_and_hashes.2.into(),
entry: entry_and_hashes.0,
created_at,
updated_at,
})),
None => Ok(None),
}
}
}
}
EntryDhtStatus::Dead => Ok(None),
_ => Ok(None),
},
_ => Ok(None),
}
}
}
fn original_action_hash_with_entry<
T: 'static + TryFrom<SerializedBytes, Error = SerializedBytesError>,
>(
action_hash: ActionHash,
get_options: GetOptions,
) -> ExternResult<Option<(T, ActionHash, EntryHash)>> {
match get(action_hash, get_options)? {
Some(record) => match record.entry().to_app_option::<T>().map_err(serialize_err)? {
Some(entry) => Ok(Some((
entry,
match record.action() {
// we DO want to return the action for the original
// instead of the updated, in our case
Action::Update(update) => update.original_action_address.clone(),
Action::Create(_) => record.action_address().clone(),
_ => {
unreachable!("Can't have returned a action for a nonexistent entry")
}
},
record.action().entry_hash().unwrap().to_owned(),
))),
None => Ok(None),
},
None => Ok(None),
}
}