1use std::collections::HashMap;
4use std::collections::HashSet;
5
6use crate::prelude::*;
7use crate::warrant::Warrant;
8use holo_hash::EntryHash;
9use holo_hash::HasHash;
10use holo_hash::{ActionHash, AgentPubKey, AnyLinkableHash};
11use holochain_integrity_types::{LinkTag, LinkTypeFilter};
12pub use holochain_serialized_bytes::prelude::*;
13
14#[derive(serde::Serialize, serde::Deserialize, PartialEq, Clone, Debug)]
33pub enum ChainQueryFilterRange {
34 Unbounded,
36 ActionSeqRange(u32, u32),
41 ActionHashRange(ActionHash, ActionHash),
45 ActionHashTerminated(ActionHash, u32),
50}
51
52impl Default for ChainQueryFilterRange {
53 fn default() -> Self {
54 Self::Unbounded
55 }
56}
57
58#[derive(
63 serde::Serialize, serde::Deserialize, SerializedBytes, Default, PartialEq, Clone, Debug,
64)]
65pub struct ChainQueryFilter {
68 pub sequence_range: ChainQueryFilterRange,
70 pub entry_type: Option<Vec<EntryType>>,
74 pub entry_hashes: Option<HashSet<EntryHash>>,
76 pub action_type: Option<Vec<ActionType>>,
80 pub include_entries: bool,
82 pub order_descending: bool,
85}
86
87#[derive(serde::Serialize, serde::Deserialize, SerializedBytes, PartialEq, Clone, Debug)]
89pub struct LinkQuery {
90 pub base: AnyLinkableHash,
92
93 pub link_type: LinkTypeFilter,
95
96 pub tag_prefix: Option<LinkTag>,
98
99 pub before: Option<Timestamp>,
101
102 pub after: Option<Timestamp>,
104
105 pub author: Option<AgentPubKey>,
107}
108
109#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, SerializedBytes)]
110pub struct AgentActivity {
112 pub valid_activity: Vec<(u32, ActionHash)>,
114 pub rejected_activity: Vec<(u32, ActionHash)>,
116 pub status: ChainStatus,
118 pub highest_observed: Option<HighestObserved>,
121 pub warrants: Vec<Warrant>,
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, SerializedBytes)]
127pub enum ActivityRequest {
129 Status,
131 Full,
133}
134
135#[derive(Clone, Debug, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
136pub struct HighestObserved {
146 pub action_seq: u32,
148 pub hash: Vec<ActionHash>,
151}
152
153#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
154#[derive(Default)]
159pub enum ChainStatus {
160 #[default]
162 Empty,
163 Valid(ChainHead),
165 Forked(ChainFork),
167 Invalid(ChainHead),
169}
170
171#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
172pub struct ChainHead {
176 pub action_seq: u32,
178 pub hash: ActionHash,
180}
181
182#[derive(Clone, Debug, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
183pub struct ChainFork {
185 pub fork_seq: u32,
187 pub first_action: ActionHash,
189 pub second_action: ActionHash,
191}
192
193impl ChainQueryFilter {
194 pub fn new() -> Self {
196 Self {
197 include_entries: false,
198 ..Self::default()
199 }
200 }
201
202 pub fn sequence_range(mut self, sequence_range: ChainQueryFilterRange) -> Self {
204 self.sequence_range = sequence_range;
205 self
206 }
207
208 pub fn entry_type(mut self, entry_type: EntryType) -> Self {
211 match self.entry_type {
212 Some(ref mut types) => {
213 types.push(entry_type);
214 }
215 None => {
216 self.entry_type = Some(vec![entry_type]);
217 }
218 }
219
220 self
221 }
222
223 pub fn entry_hashes(mut self, entry_hashes: HashSet<EntryHash>) -> Self {
225 self.entry_hashes = Some(entry_hashes);
226 self
227 }
228
229 pub fn action_type(mut self, action_type: ActionType) -> Self {
232 match self.action_type {
233 Some(ref mut types) => {
234 types.push(action_type);
235 }
236 None => {
237 self.action_type = Some(vec![action_type]);
238 }
239 }
240
241 self
242 }
243
244 pub fn include_entries(mut self, include_entries: bool) -> Self {
246 self.include_entries = include_entries;
247 self
248 }
249
250 pub fn ascending(mut self) -> Self {
252 self.order_descending = false;
253 self
254 }
255
256 pub fn descending(mut self) -> Self {
258 self.order_descending = true;
259 self
260 }
261
262 pub fn disambiguate_forks<T: ActionHashedContainer + Clone>(&self, actions: Vec<T>) -> Vec<T> {
267 match &self.sequence_range {
268 ChainQueryFilterRange::Unbounded => actions,
269 ChainQueryFilterRange::ActionSeqRange(start, end) => actions
270 .into_iter()
271 .filter(|action| {
272 *start <= action.action().action_seq() && action.action().action_seq() <= *end
273 })
274 .collect(),
275 ChainQueryFilterRange::ActionHashRange(start, end) => {
276 let mut action_hashmap = actions
277 .into_iter()
278 .map(|action| (action.action_hash().clone(), action))
279 .collect::<HashMap<ActionHash, T>>();
280 let mut filtered_actions = Vec::new();
281 let mut maybe_next_action = action_hashmap.remove(end);
282 while let Some(next_action) = maybe_next_action {
283 maybe_next_action = next_action
284 .action()
285 .prev_action()
286 .and_then(|prev_action| action_hashmap.remove(prev_action));
287 let is_start = next_action.action_hash() == start;
288 filtered_actions.push(next_action);
289 if is_start {
291 break;
292 }
293 }
294 filtered_actions
295 }
296 ChainQueryFilterRange::ActionHashTerminated(end, n) => {
297 let mut action_hashmap = actions
298 .iter()
299 .map(|action| (action.action_hash().clone(), action.clone()))
300 .collect::<HashMap<ActionHash, T>>();
301 let mut filtered_actions = Vec::new();
302 let mut maybe_next_action = action_hashmap.remove(end);
303 let mut i = 0;
304 while let Some(next_action) = maybe_next_action {
305 maybe_next_action = next_action
306 .action()
307 .prev_action()
308 .and_then(|prev_action| action_hashmap.remove(prev_action));
309 filtered_actions.push(next_action.clone());
310 if i == *n {
312 break;
313 }
314 i += 1;
315 }
316 filtered_actions
317 }
318 }
319 }
320
321 pub fn filter_actions<T: ActionHashedContainer + Clone>(&self, actions: Vec<T>) -> Vec<T> {
323 self.disambiguate_forks(actions)
324 .into_iter()
325 .filter(|action| {
326 self.action_type
327 .as_ref()
328 .map(|action_types| action_types.contains(&action.action().action_type()))
329 .unwrap_or(true)
330 && self
331 .entry_type
332 .as_ref()
333 .map(|entry_types| {
334 action
335 .action()
336 .entry_type()
337 .map(|entry_type| entry_types.contains(entry_type))
338 .unwrap_or(false)
339 })
340 .unwrap_or(true)
341 && self
342 .entry_hashes
343 .as_ref()
344 .map(|entry_hashes| match action.action().entry_hash() {
345 Some(entry_hash) => entry_hashes.contains(entry_hash),
346 None => false,
347 })
348 .unwrap_or(true)
349 })
350 .collect()
351 }
352
353 pub fn filter_records(&self, records: Vec<Record>) -> Vec<Record> {
355 let actions = self.filter_actions(
356 records
357 .iter()
358 .map(|record| record.action_hashed().clone())
359 .collect(),
360 );
361 let action_hashset = actions
362 .iter()
363 .map(|action| action.as_hash().clone())
364 .collect::<HashSet<ActionHash>>();
365 records
366 .into_iter()
367 .filter(|record| action_hashset.contains(record.action_address()))
368 .collect()
369 }
370}
371
372impl LinkQuery {
373 pub fn new(base: impl Into<AnyLinkableHash>, link_type: LinkTypeFilter) -> Self {
375 LinkQuery {
376 base: base.into(),
377 link_type,
378 tag_prefix: None,
379 before: None,
380 after: None,
381 author: None,
382 }
383 }
384
385 pub fn tag_prefix(mut self, tag_prefix: LinkTag) -> Self {
387 self.tag_prefix = Some(tag_prefix);
388 self
389 }
390
391 pub fn before(mut self, before: Timestamp) -> Self {
393 self.before = Some(before);
394 self
395 }
396
397 pub fn after(mut self, after: Timestamp) -> Self {
399 self.after = Some(after);
400 self
401 }
402
403 pub fn author(mut self, author: AgentPubKey) -> Self {
405 self.author = Some(author);
406 self
407 }
408}
409
410#[cfg(test)]
411#[cfg(feature = "fixturators")]
412mod tests {
413 use super::ChainQueryFilter;
414 use crate::action::EntryType;
415 use crate::fixt::AppEntryDefFixturator;
416 use crate::prelude::*;
417 use ::fixt::prelude::*;
418 use holo_hash::fixt::EntryHashFixturator;
419 use holo_hash::HasHash;
420
421 fn fixtures() -> [ActionHashed; 7] {
424 let entry_type_1 = EntryType::App(fixt!(AppEntryDef));
425 let entry_type_2 = EntryType::AgentPubKey;
426
427 let entry_hash_0 = fixt!(EntryHash);
428
429 let mut h0 = fixt!(Create);
430 h0.entry_type = entry_type_1.clone();
431 h0.action_seq = 0;
432 h0.entry_hash = entry_hash_0.clone();
433 let hh0 = ActionHashed::from_content_sync(h0);
434
435 let mut h1 = fixt!(Update);
436 h1.entry_type = entry_type_2.clone();
437 h1.action_seq = 1;
438 h1.prev_action = hh0.as_hash().clone();
439 let hh1 = ActionHashed::from_content_sync(h1);
440
441 let mut h2 = fixt!(CreateLink);
442 h2.action_seq = 2;
443 h2.prev_action = hh1.as_hash().clone();
444 let hh2 = ActionHashed::from_content_sync(h2);
445
446 let mut h3 = fixt!(Create);
447 h3.entry_type = entry_type_2.clone();
448 h3.action_seq = 3;
449 h3.prev_action = hh2.as_hash().clone();
450 let hh3 = ActionHashed::from_content_sync(h3);
451
452 let mut h3a = fixt!(Create);
454 h3a.entry_type = entry_type_1.clone();
455 h3a.action_seq = 3;
456 h3a.prev_action = hh2.as_hash().clone();
457 let hh3a = ActionHashed::from_content_sync(h3a);
458
459 let mut h4 = fixt!(Update);
460 h4.entry_type = entry_type_1.clone();
461 h4.entry_hash = entry_hash_0;
463 h4.action_seq = 4;
464 h4.prev_action = hh3.as_hash().clone();
465 let hh4 = ActionHashed::from_content_sync(h4);
466
467 let mut h5 = fixt!(CreateLink);
468 h5.action_seq = 5;
469 h5.prev_action = hh4.as_hash().clone();
470 let hh5 = ActionHashed::from_content_sync(h5);
471
472 [hh0, hh1, hh2, hh3, hh3a, hh4, hh5]
473 }
474
475 fn map_query(query: &ChainQueryFilter, actions: &[ActionHashed]) -> Vec<bool> {
476 let filtered = query.filter_actions(actions.to_vec());
477 actions
478 .iter()
479 .map(|h| filtered.contains(h))
480 .collect::<Vec<_>>()
481 }
482
483 #[test]
484 fn filter_by_entry_type() {
485 let actions = fixtures();
486
487 let query_1 =
488 ChainQueryFilter::new().entry_type(actions[0].entry_type().unwrap().to_owned());
489 let query_2 =
490 ChainQueryFilter::new().entry_type(actions[1].entry_type().unwrap().to_owned());
491
492 assert_eq!(
493 map_query(&query_1, &actions),
494 [true, false, false, false, true, true, false].to_vec()
495 );
496 assert_eq!(
497 map_query(&query_2, &actions),
498 [false, true, false, true, false, false, false].to_vec()
499 );
500 }
501
502 #[test]
503 fn filter_by_entry_hash() {
504 let actions = fixtures();
505
506 let query = ChainQueryFilter::new().entry_hashes(
507 vec![
508 actions[3].entry_hash().unwrap().clone(),
509 actions[5].entry_hash().unwrap().clone(),
511 ]
512 .into_iter()
513 .collect(),
514 );
515
516 assert_eq!(
517 map_query(&query, &actions),
518 vec![true, false, false, true, false, true, false]
519 );
520 }
521
522 #[test]
523 fn filter_by_action_type() {
524 let actions = fixtures();
525
526 let query_1 = ChainQueryFilter::new().action_type(actions[0].action_type());
527 let query_2 = ChainQueryFilter::new().action_type(actions[1].action_type());
528 let query_3 = ChainQueryFilter::new().action_type(actions[2].action_type());
529
530 assert_eq!(
531 map_query(&query_1, &actions),
532 [true, false, false, true, true, false, false].to_vec()
533 );
534 assert_eq!(
535 map_query(&query_2, &actions),
536 [false, true, false, false, false, true, false].to_vec()
537 );
538 assert_eq!(
539 map_query(&query_3, &actions),
540 [false, false, true, false, false, false, true].to_vec()
541 );
542 }
543
544 #[test]
545 fn filter_by_chain_sequence() {
546 let actions = fixtures();
547
548 for (sequence_range, expected, name) in vec![
549 (
550 ChainQueryFilterRange::Unbounded,
551 vec![true, true, true, true, true, true, true],
552 "unbounded",
553 ),
554 (
555 ChainQueryFilterRange::ActionSeqRange(0, 0),
556 vec![true, false, false, false, false, false, false],
557 "first only",
558 ),
559 (
560 ChainQueryFilterRange::ActionSeqRange(0, 1),
561 vec![true, true, false, false, false, false, false],
562 "several from start",
563 ),
564 (
565 ChainQueryFilterRange::ActionSeqRange(1, 2),
566 vec![false, true, true, false, false, false, false],
567 "several not start",
568 ),
569 (
570 ChainQueryFilterRange::ActionSeqRange(2, 999),
571 vec![false, false, true, true, true, true, true],
572 "exceeds chain length, not start",
573 ),
574 (
575 ChainQueryFilterRange::ActionHashRange(
576 actions[2].as_hash().clone(),
577 actions[6].as_hash().clone(),
578 ),
579 vec![false, false, true, true, false, true, true],
580 "hash bounded not 3a",
581 ),
582 (
583 ChainQueryFilterRange::ActionHashRange(
584 actions[2].as_hash().clone(),
585 actions[4].as_hash().clone(),
586 ),
587 vec![false, false, true, false, true, false, false],
588 "hash bounded 3a",
589 ),
590 (
591 ChainQueryFilterRange::ActionHashTerminated(actions[2].as_hash().clone(), 1),
592 vec![false, true, true, false, false, false, false],
593 "hash terminated not start",
594 ),
595 (
596 ChainQueryFilterRange::ActionHashTerminated(actions[2].as_hash().clone(), 0),
597 vec![false, false, true, false, false, false, false],
598 "hash terminated not start 0 prior",
599 ),
600 (
601 ChainQueryFilterRange::ActionHashTerminated(actions[5].as_hash().clone(), 7),
602 vec![true, true, true, true, false, true, false],
603 "hash terminated main chain before chain start",
604 ),
605 (
606 ChainQueryFilterRange::ActionHashTerminated(actions[4].as_hash().clone(), 7),
607 vec![true, true, true, false, true, false, false],
608 "hash terminated 3a chain before chain start",
609 ),
610 ] {
611 assert_eq!(
612 (
613 map_query(
614 &ChainQueryFilter::new().sequence_range(sequence_range),
615 &actions,
616 ),
617 name
618 ),
619 (expected, name),
620 );
621 }
622 }
623
624 #[test]
625 fn filter_by_multi() {
626 let actions = fixtures();
627
628 assert_eq!(
629 map_query(
630 &ChainQueryFilter::new()
631 .action_type(actions[0].action_type())
632 .entry_type(actions[0].entry_type().unwrap().clone())
633 .sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 0)),
634 &actions
635 ),
636 [true, false, false, false, false, false, false].to_vec()
637 );
638
639 assert_eq!(
640 map_query(
641 &ChainQueryFilter::new()
642 .action_type(actions[1].action_type())
643 .entry_type(actions[0].entry_type().unwrap().clone())
644 .sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 999)),
645 &actions
646 ),
647 [false, false, false, false, false, true, false].to_vec()
648 );
649
650 assert_eq!(
651 map_query(
652 &ChainQueryFilter::new()
653 .entry_type(actions[0].entry_type().unwrap().clone())
654 .sequence_range(ChainQueryFilterRange::ActionSeqRange(0, 999)),
655 &actions
656 ),
657 [true, false, false, false, true, true, false].to_vec()
658 );
659 }
660
661 #[test]
662 fn filter_by_multiple_action_types() {
663 let actions = fixtures();
664
665 assert_eq!(
667 map_query(
668 &ChainQueryFilter::new()
669 .action_type(actions[0].action_type())
670 .action_type(actions[1].action_type()),
671 &actions
672 ),
673 [true, true, false, true, true, true, false].to_vec()
674 );
675
676 assert_eq!(
678 map_query(
679 &ChainQueryFilter::new().action_type(actions[0].action_type()),
680 &actions
681 ),
682 [true, false, false, true, true, false, false].to_vec()
683 );
684 }
685
686 #[test]
687 fn filter_by_multiple_entry_types() {
688 let actions = fixtures();
689
690 assert_eq!(
692 map_query(
693 &ChainQueryFilter::new()
694 .entry_type(actions[0].entry_type().unwrap().clone())
695 .entry_type(actions[1].entry_type().unwrap().clone()),
696 &actions
697 ),
698 [true, true, false, true, true, true, false].to_vec()
699 );
700
701 assert_eq!(
703 map_query(
704 &ChainQueryFilter::new().entry_type(actions[0].entry_type().unwrap().clone()),
705 &actions
706 ),
707 [true, false, false, false, true, true, false].to_vec()
708 );
709 }
710}