use crate::cesr::Versionage;
use crate::keri::core::serdering::{SadValue, SerderKERI};
use crate::keri::{versify, Ilks, Kinds};
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use std::error::Error;
pub struct QueryEventBuilder {
route: String,
reply_route: String,
query: Option<IndexMap<String, SadValue>>,
stamp: Option<String>,
version: String,
kind: String,
}
impl QueryEventBuilder {
pub fn new() -> Self {
Self {
route: String::new(),
reply_route: String::new(),
query: None,
stamp: None,
version: "KERI10JSON000000_".to_string(),
kind: "JSON".to_string(),
}
}
pub fn with_route(mut self, route: String) -> Self {
self.route = route;
self
}
pub fn with_reply_route(mut self, reply_route: String) -> Self {
self.reply_route = reply_route;
self
}
pub fn with_query(mut self, query: IndexMap<String, SadValue>) -> Self {
self.query = Some(query);
self
}
pub fn with_stamp(mut self, stamp: String) -> Self {
self.stamp = Some(stamp);
self
}
pub fn with_version(mut self, version: String) -> Self {
self.version = version;
self
}
pub fn with_kind(mut self, kind: String) -> Self {
self.kind = kind;
self
}
pub fn build(self) -> Result<SerderKERI, Box<dyn Error>> {
if !Kinds::contains(&self.kind) {
return Err(format!("Invalid kind = {} for qry.", self.kind).into());
}
let vs = versify("KERI", &Versionage::from(self.version), &self.kind, 0)?;
let timestamp = match self.stamp {
Some(ts) => ts,
None => {
let now: DateTime<Utc> = Utc::now();
now.to_rfc3339()
}
};
let mut ked = IndexMap::new();
ked.insert("v".to_string(), SadValue::String(vs));
ked.insert("t".to_string(), SadValue::String(Ilks::QRY.to_string()));
ked.insert("d".to_string(), SadValue::String("".to_string()));
ked.insert("dt".to_string(), SadValue::String(timestamp));
ked.insert("r".to_string(), SadValue::String(self.route));
ked.insert("rr".to_string(), SadValue::String(self.reply_route));
if let Some(query_data) = self.query {
ked.insert("q".to_string(), SadValue::Object(query_data));
} else {
ked.insert("q".to_string(), SadValue::Object(IndexMap::new()));
}
let serder = SerderKERI::from_sad_and_saids(&ked, None)?;
Ok(serder)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keri::core::serdering::Serder;
use std::error::Error;
#[test]
fn test_query_event_builder_basic() -> Result<(), Box<dyn Error>> {
let serder = QueryEventBuilder::new()
.with_route("logs".to_string())
.with_reply_route("log/processor".to_string())
.build()?;
let ked = serder.ked();
assert_eq!(ked["t"].as_str().unwrap(), Ilks::QRY);
assert_eq!(ked["r"].as_str().unwrap(), "logs");
assert_eq!(ked["rr"].as_str().unwrap(), "log/processor");
assert!(ked.get("dt").is_some());
match &ked["q"] {
SadValue::Object(obj) => assert!(obj.is_empty()),
_ => panic!("Expected q field to be an object"),
}
Ok(())
}
#[test]
fn test_query_event_with_query_data() -> Result<(), Box<dyn Error>> {
let mut query_data = IndexMap::new();
query_data.insert(
"i".to_string(),
SadValue::String("EaU6JR2nmwyZ-i0d8JZAoTNZH3ULvYAfSVPzhzS6b5CM".to_string()),
);
query_data.insert("sn".to_string(), SadValue::String("5".to_string()));
query_data.insert(
"dt".to_string(),
SadValue::String("2020-08-01T12:20:05.123456+00:00".to_string()),
);
let timestamp = "2020-08-22T17:50:12.988921+00:00".to_string();
let serder = QueryEventBuilder::new()
.with_route("logs".to_string())
.with_reply_route("log/processor".to_string())
.with_query(query_data)
.with_stamp(timestamp.clone())
.build()?;
let ked = serder.ked();
assert_eq!(ked["dt"].as_str().unwrap(), timestamp);
let q = match &ked["q"] {
SadValue::Object(obj) => obj,
_ => panic!("Expected q field to be an object"),
};
assert_eq!(
q["i"].as_str().unwrap(),
"EaU6JR2nmwyZ-i0d8JZAoTNZH3ULvYAfSVPzhzS6b5CM"
);
assert_eq!(q["sn"].as_str().unwrap(), "5");
assert_eq!(
q["dt"].as_str().unwrap(),
"2020-08-01T12:20:05.123456+00:00"
);
Ok(())
}
#[test]
fn test_query_event_custom_version_kind() -> Result<(), Box<dyn Error>> {
let serder = QueryEventBuilder::new()
.with_route("logs".to_string())
.with_version("KERI10".to_string())
.with_kind("CBOR".to_string())
.build()?;
let ked = serder.ked();
let version = ked["v"].as_str().unwrap();
assert!(version.starts_with("KERI10CBOR"));
Ok(())
}
#[test]
fn test_query_event_invalid_kind() -> Result<(), Box<dyn Error>> {
let result = QueryEventBuilder::new()
.with_kind("INVALID".to_string())
.build();
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("Invalid kind"));
Ok(())
}
#[test]
fn test_query_event_said_derivation() -> Result<(), Box<dyn Error>> {
let serder = QueryEventBuilder::new()
.with_route("logs".to_string())
.build()?;
let said = serder.said().expect("Failed to get SAID");
assert!(said.starts_with('E'));
assert_eq!(serder.ked()["d"].as_str().unwrap(), said);
Ok(())
}
#[test]
fn test_query_matches_python_example() -> Result<(), Box<dyn Error>> {
let mut query_data = IndexMap::new();
query_data.insert(
"i".to_string(),
SadValue::String("EaU6JR2nmwyZ-i0d8JZAoTNZH3ULvYAfSVPzhzS6b5CM".to_string()),
);
query_data.insert("sn".to_string(), SadValue::String("5".to_string()));
query_data.insert(
"dt".to_string(),
SadValue::String("2020-08-01T12:20:05.123456+00:00".to_string()),
);
let serder = QueryEventBuilder::new()
.with_route("logs".to_string())
.with_reply_route("log/processor".to_string())
.with_query(query_data)
.with_stamp("2020-08-22T17:50:12.988921+00:00".to_string())
.build()?;
let ked = serder.ked();
assert_eq!(ked["t"].as_str().unwrap(), Ilks::QRY);
assert_eq!(
ked["dt"].as_str().unwrap(),
"2020-08-22T17:50:12.988921+00:00"
);
assert_eq!(ked["r"].as_str().unwrap(), "logs");
assert_eq!(ked["rr"].as_str().unwrap(), "log/processor");
let q = match &ked["q"] {
SadValue::Object(obj) => obj,
_ => panic!("Expected q field to be an object"),
};
assert_eq!(
q["i"].as_str().unwrap(),
"EaU6JR2nmwyZ-i0d8JZAoTNZH3ULvYAfSVPzhzS6b5CM"
);
assert_eq!(q["sn"].as_str().unwrap(), "5");
assert_eq!(
q["dt"].as_str().unwrap(),
"2020-08-01T12:20:05.123456+00:00"
);
Ok(())
}
#[test]
fn test_query_event_builder_serialization() -> Result<(), Box<dyn Error>> {
let mut query_data = IndexMap::new();
query_data.insert(
"i".to_string(),
SadValue::String("DAvCLRr5luWmp7keDvDuLP0kIqcyBYq79b3Dho1QvrjI".to_string()),
);
let timestamp = "2021-01-01T00:00:00.000000+00:00".to_string();
let serder = QueryEventBuilder::new()
.with_route("log".to_string())
.with_query(query_data)
.with_stamp(timestamp)
.build()?;
let raw = serder.raw();
let expected = b"{\"v\":\"KERI10JSON0000c9_\",\"t\":\"qry\",\"d\":\"EGN68_seecuzXQO15FFGJLVwZCBCPYW-hy29fjWWPQbp\",\"dt\":\"2021-01-01T00:00:00.000000+00:00\",\"r\":\"log\",\"rr\":\"\",\"q\":{\"i\":\"DAvCLRr5luWmp7keDvDuLP0kIqcyBYq79b3Dho1QvrjI\"}}";
assert_eq!(raw, expected);
Ok(())
}
}