use std::sync::Arc;
use affinidi_messaging_sdk::errors::ATMError;
use affinidi_messaging_sdk::protocols::tsp::{
PersistentRelationshipStore, RelationshipKv, RelationshipStore,
};
use async_trait::async_trait;
use crate::server::error::AppError;
use crate::server::store::{KS_TSP_RELATIONSHIPS, KeyspaceHandle, Store};
pub struct KeyspaceRelationshipKv {
ks: KeyspaceHandle,
}
impl KeyspaceRelationshipKv {
pub fn new(ks: KeyspaceHandle) -> Self {
Self { ks }
}
}
fn to_atm(e: AppError) -> ATMError {
ATMError::SDKError(format!("tsp relationship store: {e}"))
}
#[async_trait]
impl RelationshipKv for KeyspaceRelationshipKv {
async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, ATMError> {
self.ks.get_raw(key.to_vec()).await.map_err(to_atm)
}
async fn put(&self, key: &[u8], value: &[u8]) -> Result<(), ATMError> {
self.ks
.insert_raw(key.to_vec(), value.to_vec())
.await
.map_err(to_atm)
}
async fn delete(&self, key: &[u8]) -> Result<(), ATMError> {
self.ks.remove(key.to_vec()).await.map_err(to_atm)
}
async fn scan_prefix(&self, prefix: &[u8]) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ATMError> {
self.ks
.prefix_iter_raw(prefix.to_vec())
.await
.map_err(to_atm)
}
}
pub type KeyspaceRelationshipStore = PersistentRelationshipStore<KeyspaceRelationshipKv>;
pub fn open_relationship_store(store: &Store) -> Result<KeyspaceRelationshipStore, AppError> {
let ks = store.keyspace(KS_TSP_RELATIONSHIPS)?;
Ok(PersistentRelationshipStore::new(
KeyspaceRelationshipKv::new(ks),
))
}
pub fn build_relationship_store(store: &Store) -> Result<Arc<dyn RelationshipStore>, AppError> {
Ok(Arc::new(open_relationship_store(store)?))
}
#[cfg(all(test, feature = "store-fjall"))]
mod tests {
use super::*;
use crate::server::config::StoreConfig;
#[tokio::test]
async fn byte_backend_round_trips_through_a_real_keyspace() {
let dir = tempfile::tempdir().expect("temp dir");
let store_config = StoreConfig {
data_dir: dir.path().to_path_buf(),
..StoreConfig::default()
};
let store = Store::open(&store_config).await.expect("open store");
let kv = KeyspaceRelationshipKv::new(
store.keyspace(KS_TSP_RELATIONSHIPS).expect("open keyspace"),
);
let key = b"tsp-rel/v1/\x01peer".as_slice();
let value = b"opaque-sdk-encoded-record".as_slice();
assert_eq!(kv.get(key).await.expect("get absent"), None);
kv.put(key, value).await.expect("put");
assert_eq!(
kv.get(key).await.expect("get present").as_deref(),
Some(value),
"a stored value must read back byte-for-byte"
);
kv.delete(key).await.expect("delete");
assert_eq!(
kv.get(key).await.expect("get after delete"),
None,
"a deleted key must be absent"
);
kv.delete(key).await.expect("delete absent is a no-op");
let _store: Arc<dyn RelationshipStore> =
build_relationship_store(&store).expect("build relationship store");
}
}