This commit is contained in:
Ellie Huxtable 2023-12-09 17:36:50 +00:00
parent eadce83a57
commit b99de3c4d7
5 changed files with 80 additions and 13 deletions

View File

@ -312,9 +312,8 @@ impl History {
#[cfg(test)]
mod tests {
use atuin_common::record::DecryptedData;
use regex::RegexSet;
use time::{macros::datetime, OffsetDateTime};
use time::macros::datetime;
use crate::{history::HISTORY_VERSION, settings::Settings};

View File

@ -151,11 +151,30 @@ impl KvStore {
// use as a write-through cache to avoid constant rebuilds.
pub async fn build_kv(
&self,
_store: &impl Store,
_encryption_key: &[u8; 32],
store: &impl Store,
encryption_key: &[u8; 32],
) -> Result<BTreeMap<String, BTreeMap<String, String>>> {
let map = BTreeMap::new();
// TODO: implement
// get the status of all stores
let mut tagged = store.all_tagged(KV_TAG).await?;
// iterate through all tags and play each KV record at a time
for (host, record) in tagged {
let decrypted = match record.version.as_str() {
KV_VERSION => record.decrypt::<PASETO_V4>(encryption_key)?,
version => bail!("unknown version {version:?}"),
};
let kv = KvRecord::deserialize(&decrypted.data, &decrypted.version)?;
let next = store.next(host, KV_TAG, decrypted.idx, 1).await?;
write a function that iterates the kv records in order and builds a map
maybe next_record(tag) that returns the next of that tag in time, ignoring host?
then write some tests for this new sync
also see what happens if we run the new server but try and sync with v17 client. I imagine `atuin sync` breaks, but auto sync is fine. maybe version the api now.
}
Ok(map)
}

View File

@ -2,8 +2,8 @@
// Multiple stores of multiple types are all stored in one chonky table (for now), and we just index
// by tag/host
use std::path::Path;
use std::str::FromStr;
use std::{collections::HashMap, path::Path};
use async_trait::async_trait;
use eyre::{eyre, Result};
@ -190,7 +190,7 @@ impl Store for SqliteStore {
) -> Result<Option<Record<EncryptedData>>> {
let res = sqlx::query("select * from store where idx = ?1 and host = ?2 and tag = ?3")
.bind(idx as i64)
.bind(host)
.bind(host.0.as_hyphenated().to_string())
.bind(tag)
.map(Self::query_row)
.fetch_one(&self.pool)
@ -227,14 +227,22 @@ impl Store for SqliteStore {
Ok(status)
}
async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>> {
async fn all_tagged(&self, tag: &str) -> Result<HashMap<HostId, Record<EncryptedData>>> {
let res = sqlx::query("select * from store where idx = 0 and tag = ?1")
.bind(tag)
.map(Self::query_row)
.fetch_all(&self.pool)
.await?;
Ok(res)
let mut ret = HashMap::new();
for i in res {
assert!(ret.get(&i.host.id).is_none());
ret.insert(i.host.id, i);
}
Ok(ret)
}
}
@ -307,6 +315,24 @@ mod tests {
);
}
#[tokio::test]
async fn first() {
let db = SqliteStore::new(":memory:").await.unwrap();
let record = test_record();
db.push(&record).await.unwrap();
let first = db
.first(record.host.id, record.tag.as_str())
.await
.expect("failed to get store len");
assert_eq!(
first.unwrap().id,
record.id,
"expected to get back the same record that was inserted"
);
}
#[tokio::test]
async fn len() {
let db = SqliteStore::new(":memory:").await.unwrap();

View File

@ -1,3 +1,5 @@
use std::collections::HashMap;
use async_trait::async_trait;
use eyre::Result;
@ -48,5 +50,5 @@ pub trait Store {
/// Get every start record for a given tag, regardless of host.
/// Useful when actually operating on synchronized data, and will often have conflict
/// resolution applied.
async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>>;
async fn all_tagged(&self, tag: &str) -> Result<HashMap<HostId, Record<EncryptedData>>>;
}

View File

@ -2,6 +2,7 @@ use clap::Subcommand;
use eyre::Result;
use atuin_client::{record::store::Store, settings::Settings};
use time::OffsetDateTime;
#[derive(Subcommand, Debug)]
#[command(infer_subcommands = true)]
@ -19,7 +20,8 @@ impl Cmd {
let status = store.status().await?;
for (host, store) in &status.hosts {
// TODO: should probs build some data structure and then pretty-print it or smth
for (host, st) in &status.hosts {
let host_string = if host == &host_id {
format!("host: {} <- CURRENT HOST", host.0.as_hyphenated())
} else {
@ -28,8 +30,27 @@ impl Cmd {
println!("{host_string}");
for (tag, idx) in store {
println!("\tstore: {tag} at {idx}");
for (tag, idx) in st {
println!("\tstore: {tag}");
let first = store.first(*host, tag).await?;
let last = store.last(*host, tag).await?;
println!("\t\tidx: {idx}");
if let Some(first) = first {
println!("\t\tfirst: {}", first.id.0.as_hyphenated().to_string());
let time = OffsetDateTime::from_unix_timestamp_nanos(first.timestamp as i128)?;
println!("\t\t\tcreated: {}", time.to_string());
}
if let Some(last) = last {
println!("\t\tlast: {}", last.id.0.as_hyphenated().to_string());
let time = OffsetDateTime::from_unix_timestamp_nanos(last.timestamp as i128)?;
println!("\t\t\tcreated: {:?}", time.to_string());
}
}
println!();