Refactor core into a type with methods

This introduces a `Server` type that tracks the config and storage.
This commit is contained in:
Dustin J. Mitchell 2024-11-17 18:49:16 -05:00
parent 2b1ad12a79
commit 5a704729d7
No known key found for this signature in database
9 changed files with 583 additions and 1745 deletions

View file

@ -1,4 +1,4 @@
use crate::storage::{Client, Snapshot, StorageTxn}; use crate::storage::{Client, Snapshot, Storage, StorageTxn};
use chrono::Utc; use chrono::Utc;
use uuid::Uuid; use uuid::Uuid;
@ -32,15 +32,6 @@ impl Default for ServerConfig {
} }
} }
impl ServerConfig {
pub fn from_args(snapshot_days: i64, snapshot_versions: u32) -> anyhow::Result<ServerConfig> {
Ok(ServerConfig {
snapshot_days,
snapshot_versions,
})
}
}
/// Response to get_child_version. See the protocol documentation. /// Response to get_child_version. See the protocol documentation.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum GetVersionResult { pub enum GetVersionResult {
@ -53,40 +44,6 @@ pub enum GetVersionResult {
}, },
} }
/// Implementation of the GetChildVersion protocol transaction.
pub fn get_child_version<'a>(
mut txn: Box<dyn StorageTxn + 'a>,
_config: &ServerConfig,
client_id: ClientId,
client: Client,
parent_version_id: VersionId,
) -> anyhow::Result<GetVersionResult> {
// If a version with parentVersionId equal to the requested parentVersionId exists, it is returned.
if let Some(version) = txn.get_version_by_parent(client_id, parent_version_id)? {
return Ok(GetVersionResult::Success {
version_id: version.version_id,
parent_version_id: version.parent_version_id,
history_segment: version.history_segment,
});
}
// Return NotFound if an AddVersion with this parent_version_id would succeed, and otherwise
// return Gone.
//
// AddVersion will succeed if either
// - the requested parent version is the latest version; or
// - there is no latest version, meaning there are no versions stored for this client
Ok(
if client.latest_version_id == parent_version_id
|| client.latest_version_id == NIL_VERSION_ID
{
GetVersionResult::NotFound
} else {
GetVersionResult::Gone
},
)
}
/// Response to add_version /// Response to add_version
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum AddVersionResult { pub enum AddVersionResult {
@ -133,15 +90,66 @@ impl SnapshotUrgency {
} }
} }
/// A server implementing the TaskChampion sync protocol.
pub struct Server {
config: ServerConfig,
storage: Box<dyn Storage>,
}
impl Server {
pub fn new<ST: Storage + 'static>(config: ServerConfig, storage: ST) -> Self {
Self {
config,
storage: Box::new(storage),
}
}
/// Implementation of the GetChildVersion protocol transaction.
pub fn get_child_version(
&self,
client_id: ClientId,
client: Client,
parent_version_id: VersionId,
) -> anyhow::Result<GetVersionResult> {
let mut txn = self.storage.txn()?;
// If a version with parentVersionId equal to the requested parentVersionId exists, it is
// returned.
if let Some(version) = txn.get_version_by_parent(client_id, parent_version_id)? {
return Ok(GetVersionResult::Success {
version_id: version.version_id,
parent_version_id: version.parent_version_id,
history_segment: version.history_segment,
});
}
// Return NotFound if an AddVersion with this parent_version_id would succeed, and
// otherwise return Gone.
//
// AddVersion will succeed if either
// - the requested parent version is the latest version; or
// - there is no latest version, meaning there are no versions stored for this client
Ok(
if client.latest_version_id == parent_version_id
|| client.latest_version_id == NIL_VERSION_ID
{
GetVersionResult::NotFound
} else {
GetVersionResult::Gone
},
)
}
/// Implementation of the AddVersion protocol transaction /// Implementation of the AddVersion protocol transaction
pub fn add_version<'a>( pub fn add_version(
mut txn: Box<dyn StorageTxn + 'a>, &self,
config: &ServerConfig,
client_id: ClientId, client_id: ClientId,
client: Client, client: Client,
parent_version_id: VersionId, parent_version_id: VersionId,
history_segment: HistorySegment, history_segment: HistorySegment,
) -> anyhow::Result<(AddVersionResult, SnapshotUrgency)> { ) -> anyhow::Result<(AddVersionResult, SnapshotUrgency)> {
let mut txn = self.storage.txn()?;
log::debug!( log::debug!(
"add_version(client_id: {}, parent_version_id: {})", "add_version(client_id: {}, parent_version_id: {})",
client_id, client_id,
@ -149,7 +157,9 @@ pub fn add_version<'a>(
); );
// check if this version is acceptable, under the protection of the transaction // check if this version is acceptable, under the protection of the transaction
if client.latest_version_id != NIL_VERSION_ID && parent_version_id != client.latest_version_id { if client.latest_version_id != NIL_VERSION_ID
&& parent_version_id != client.latest_version_id
{
log::debug!("add_version request rejected: mismatched latest_version_id"); log::debug!("add_version request rejected: mismatched latest_version_id");
return Ok(( return Ok((
AddVersionResult::ExpectedParentVersion(client.latest_version_id), AddVersionResult::ExpectedParentVersion(client.latest_version_id),
@ -172,14 +182,14 @@ pub fn add_version<'a>(
let time_urgency = match client.snapshot { let time_urgency = match client.snapshot {
None => SnapshotUrgency::High, None => SnapshotUrgency::High,
Some(Snapshot { timestamp, .. }) => { Some(Snapshot { timestamp, .. }) => {
SnapshotUrgency::for_days(config, (Utc::now() - timestamp).num_days()) SnapshotUrgency::for_days(&self.config, (Utc::now() - timestamp).num_days())
} }
}; };
let version_urgency = match client.snapshot { let version_urgency = match client.snapshot {
None => SnapshotUrgency::High, None => SnapshotUrgency::High,
Some(Snapshot { versions_since, .. }) => { Some(Snapshot { versions_since, .. }) => {
SnapshotUrgency::for_versions_since(config, versions_since) SnapshotUrgency::for_versions_since(&self.config, versions_since)
} }
}; };
@ -190,14 +200,15 @@ pub fn add_version<'a>(
} }
/// Implementation of the AddSnapshot protocol transaction /// Implementation of the AddSnapshot protocol transaction
pub fn add_snapshot<'a>( pub fn add_snapshot(
mut txn: Box<dyn StorageTxn + 'a>, &self,
_config: &ServerConfig,
client_id: ClientId, client_id: ClientId,
client: Client, client: Client,
version_id: VersionId, version_id: VersionId,
data: Vec<u8>, data: Vec<u8>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut txn = self.storage.txn()?;
log::debug!( log::debug!(
"add_snapshot(client_id: {}, version_id: {})", "add_snapshot(client_id: {}, version_id: {})",
client_id, client_id,
@ -275,12 +286,13 @@ pub fn add_snapshot<'a>(
} }
/// Implementation of the GetSnapshot protocol transaction /// Implementation of the GetSnapshot protocol transaction
pub fn get_snapshot<'a>( pub fn get_snapshot(
mut txn: Box<dyn StorageTxn + 'a>, &self,
_config: &ServerConfig,
client_id: ClientId, client_id: ClientId,
client: Client, client: Client,
) -> anyhow::Result<Option<(Uuid, Vec<u8>)>> { ) -> anyhow::Result<Option<(Uuid, Vec<u8>)>> {
let mut txn = self.storage.txn()?;
Ok(if let Some(snap) = client.snapshot { Ok(if let Some(snap) = client.snapshot {
txn.get_snapshot_data(client_id, snap.version_id)? txn.get_snapshot_data(client_id, snap.version_id)?
.map(|data| (snap.version_id, data)) .map(|data| (snap.version_id, data))
@ -289,16 +301,103 @@ pub fn get_snapshot<'a>(
}) })
} }
/// Convenience method to get a transaction for the embedded storage.
pub fn txn(&self) -> anyhow::Result<Box<dyn StorageTxn + '_>> {
self.storage.txn()
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::inmemory::InMemoryStorage; use crate::inmemory::InMemoryStorage;
use crate::storage::{Snapshot, Storage}; use crate::storage::{Snapshot, Storage, StorageTxn};
use chrono::{Duration, TimeZone, Utc}; use chrono::{Duration, TimeZone, Utc};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
fn init_logging() { fn setup<INIT, RES>(init: INIT) -> anyhow::Result<(Server, RES)>
where
INIT: FnOnce(&mut dyn StorageTxn) -> anyhow::Result<RES>,
{
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
let storage = InMemoryStorage::new();
let res = init(storage.txn()?.as_mut())?;
Ok((Server::new(ServerConfig::default(), storage), res))
}
/// Utility setup function for add_version tests
fn av_setup(
num_versions: u32,
snapshot_version: Option<u32>,
snapshot_days_ago: Option<i64>,
) -> anyhow::Result<(Server, Uuid, Client, Vec<Uuid>)> {
let (server, (client_id, client, versions)) = setup(|txn| {
let client_id = Uuid::new_v4();
let mut versions = vec![];
let mut version_id = Uuid::nil();
txn.new_client(client_id, Uuid::nil())?;
for vnum in 0..num_versions {
let parent_version_id = version_id;
version_id = Uuid::new_v4();
versions.push(version_id);
txn.add_version(
client_id,
version_id,
parent_version_id,
vec![0, 0, vnum as u8],
)?;
if Some(vnum) == snapshot_version {
txn.set_snapshot(
client_id,
Snapshot {
version_id,
versions_since: 0,
timestamp: Utc::now() - Duration::days(snapshot_days_ago.unwrap_or(0)),
},
vec![vnum as u8],
)?;
}
}
let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client, versions))
})?;
Ok((server, client_id, client, versions))
}
/// Utility function to check the results of an add_version call
fn av_success_check(
server: &Server,
client_id: Uuid,
existing_versions: &[Uuid],
result: (AddVersionResult, SnapshotUrgency),
expected_history: Vec<u8>,
expected_urgency: SnapshotUrgency,
) -> anyhow::Result<()> {
if let AddVersionResult::Ok(new_version_id) = result.0 {
// check that it invented a new version ID
for v in existing_versions {
assert_ne!(&new_version_id, v);
}
// verify that the storage was updated
let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap();
assert_eq!(client.latest_version_id, new_version_id);
let parent_version_id = existing_versions.last().cloned().unwrap_or_else(Uuid::nil);
let version = txn.get_version(client_id, new_version_id)?.unwrap();
assert_eq!(version.version_id, new_version_id);
assert_eq!(version.parent_version_id, parent_version_id);
assert_eq!(version.history_segment, expected_history);
} else {
panic!("did not get Ok from add_version: {:?}", result);
}
assert_eq!(result.1, expected_urgency);
Ok(())
} }
#[test] #[test]
@ -347,23 +446,16 @@ mod test {
#[test] #[test]
fn get_child_version_not_found_initial_nil() -> anyhow::Result<()> { fn get_child_version_not_found_initial_nil() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
txn.new_client(client_id, NIL_VERSION_ID)?; txn.new_client(client_id, NIL_VERSION_ID)?;
// when no latest version exists, the first version is NotFound
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client))
})?;
// when no latest version exists, the first version is NotFound
assert_eq!( assert_eq!(
get_child_version( server.get_child_version(client_id, client, NIL_VERSION_ID)?,
txn,
&ServerConfig::default(),
client_id,
client,
NIL_VERSION_ID
)?,
GetVersionResult::NotFound GetVersionResult::NotFound
); );
Ok(()) Ok(())
@ -371,25 +463,18 @@ mod test {
#[test] #[test]
fn get_child_version_not_found_initial_continuing() -> anyhow::Result<()> { fn get_child_version_not_found_initial_continuing() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
txn.new_client(client_id, NIL_VERSION_ID)?; txn.new_client(client_id, NIL_VERSION_ID)?;
let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client))
})?;
// when no latest version exists, _any_ child version is NOT_FOUND. This allows syncs to // when no latest version exists, _any_ child version is NOT_FOUND. This allows syncs to
// start to a new server even if the client already has been uploading to another service. // start to a new server even if the client already has been uploading to another service.
let client = txn.get_client(client_id)?.unwrap();
assert_eq!( assert_eq!(
get_child_version( server.get_child_version(client_id, client, Uuid::new_v4(),)?,
txn,
&ServerConfig::default(),
client_id,
client,
Uuid::new_v4(),
)?,
GetVersionResult::NotFound GetVersionResult::NotFound
); );
Ok(()) Ok(())
@ -397,26 +482,19 @@ mod test {
#[test] #[test]
fn get_child_version_not_found_up_to_date() -> anyhow::Result<()> { fn get_child_version_not_found_up_to_date() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, parent_version_id)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4();
// add a parent version, but not the requested child version // add a parent version, but not the requested child version
let client_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
txn.new_client(client_id, parent_version_id)?; txn.new_client(client_id, parent_version_id)?;
txn.add_version(client_id, parent_version_id, NIL_VERSION_ID, vec![])?; txn.add_version(client_id, parent_version_id, NIL_VERSION_ID, vec![])?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client, parent_version_id))
})?;
assert_eq!( assert_eq!(
get_child_version( server.get_child_version(client_id, client, parent_version_id)?,
txn,
&ServerConfig::default(),
client_id,
client,
parent_version_id
)?,
GetVersionResult::NotFound GetVersionResult::NotFound
); );
Ok(()) Ok(())
@ -424,10 +502,7 @@ mod test {
#[test] #[test]
fn get_child_version_gone_not_latest() -> anyhow::Result<()> { fn get_child_version_gone_not_latest() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
// Add a parent version, but not the requested parent version // Add a parent version, but not the requested parent version
@ -436,14 +511,11 @@ mod test {
txn.add_version(client_id, parent_version_id, NIL_VERSION_ID, vec![])?; txn.add_version(client_id, parent_version_id, NIL_VERSION_ID, vec![])?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client))
})?;
assert_eq!( assert_eq!(
get_child_version( server.get_child_version(client_id, client, Uuid::new_v4(),)?,
txn,
&ServerConfig::default(),
client_id,
client,
Uuid::new_v4(),
)?,
GetVersionResult::Gone GetVersionResult::Gone
); );
Ok(()) Ok(())
@ -451,10 +523,8 @@ mod test {
#[test] #[test]
fn get_child_version_found() -> anyhow::Result<()> { fn get_child_version_found() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, version_id, parent_version_id, history_segment)) =
setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
@ -469,14 +539,16 @@ mod test {
)?; )?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
assert_eq!( Ok((
get_child_version(
txn,
&ServerConfig::default(),
client_id, client_id,
client, client,
parent_version_id version_id,
)?, parent_version_id,
history_segment,
))
})?;
assert_eq!(
server.get_child_version(client_id, client, parent_version_id)?,
GetVersionResult::Success { GetVersionResult::Success {
version_id, version_id,
parent_version_id, parent_version_id,
@ -486,104 +558,20 @@ mod test {
Ok(()) Ok(())
} }
/// Utility setup function for add_version tests
fn av_setup(
storage: &InMemoryStorage,
num_versions: u32,
snapshot_version: Option<u32>,
snapshot_days_ago: Option<i64>,
) -> anyhow::Result<(Uuid, Vec<Uuid>)> {
init_logging();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4();
let mut versions = vec![];
let mut version_id = Uuid::nil();
txn.new_client(client_id, Uuid::nil())?;
for vnum in 0..num_versions {
let parent_version_id = version_id;
version_id = Uuid::new_v4();
versions.push(version_id);
txn.add_version(
client_id,
version_id,
parent_version_id,
vec![0, 0, vnum as u8],
)?;
if Some(vnum) == snapshot_version {
txn.set_snapshot(
client_id,
Snapshot {
version_id,
versions_since: 0,
timestamp: Utc::now() - Duration::days(snapshot_days_ago.unwrap_or(0)),
},
vec![vnum as u8],
)?;
}
}
Ok((client_id, versions))
}
/// Utility function to check the results of an add_version call
fn av_success_check(
storage: &InMemoryStorage,
client_id: Uuid,
existing_versions: &[Uuid],
result: (AddVersionResult, SnapshotUrgency),
expected_history: Vec<u8>,
expected_urgency: SnapshotUrgency,
) -> anyhow::Result<()> {
if let AddVersionResult::Ok(new_version_id) = result.0 {
// check that it invented a new version ID
for v in existing_versions {
assert_ne!(&new_version_id, v);
}
// verify that the storage was updated
let mut txn = storage.txn()?;
let client = txn.get_client(client_id)?.unwrap();
assert_eq!(client.latest_version_id, new_version_id);
let parent_version_id = existing_versions.last().cloned().unwrap_or_else(Uuid::nil);
let version = txn.get_version(client_id, new_version_id)?.unwrap();
assert_eq!(version.version_id, new_version_id);
assert_eq!(version.parent_version_id, parent_version_id);
assert_eq!(version.history_segment, expected_history);
} else {
panic!("did not get Ok from add_version: {:?}", result);
}
assert_eq!(result.1, expected_urgency);
Ok(())
}
#[test] #[test]
fn add_version_conflict() -> anyhow::Result<()> { fn add_version_conflict() -> anyhow::Result<()> {
let storage = InMemoryStorage::new(); let (server, client_id, client, versions) = av_setup(3, None, None)?;
let (client_id, versions) = av_setup(&storage, 3, None, None)?;
let mut txn = storage.txn()?;
let client = txn.get_client(client_id)?.unwrap();
// try to add a child of a version other than the latest // try to add a child of a version other than the latest
assert_eq!( assert_eq!(
add_version( server
txn, .add_version(client_id, client, versions[1], vec![3, 6, 9])?
&ServerConfig::default(),
client_id,
client,
versions[1],
vec![3, 6, 9]
)?
.0, .0,
AddVersionResult::ExpectedParentVersion(versions[2]) AddVersionResult::ExpectedParentVersion(versions[2])
); );
// verify that the storage wasn't updated // verify that the storage wasn't updated
txn = storage.txn()?; let mut txn = server.txn()?;
assert_eq!( assert_eq!(
txn.get_client(client_id)?.unwrap().latest_version_id, txn.get_client(client_id)?.unwrap().latest_version_id,
versions[2] versions[2]
@ -595,23 +583,12 @@ mod test {
#[test] #[test]
fn add_version_with_existing_history() -> anyhow::Result<()> { fn add_version_with_existing_history() -> anyhow::Result<()> {
let storage = InMemoryStorage::new(); let (server, client_id, client, versions) = av_setup(1, None, None)?;
let (client_id, versions) = av_setup(&storage, 1, None, None)?;
let mut txn = storage.txn()?; let result = server.add_version(client_id, client, versions[0], vec![3, 6, 9])?;
let client = txn.get_client(client_id)?.unwrap();
let result = add_version(
txn,
&ServerConfig::default(),
client_id,
client,
versions[0],
vec![3, 6, 9],
)?;
av_success_check( av_success_check(
&storage, &server,
client_id, client_id,
&versions, &versions,
result, result,
@ -625,24 +602,13 @@ mod test {
#[test] #[test]
fn add_version_with_no_history() -> anyhow::Result<()> { fn add_version_with_no_history() -> anyhow::Result<()> {
let storage = InMemoryStorage::new(); let (server, client_id, client, versions) = av_setup(0, None, None)?;
let (client_id, versions) = av_setup(&storage, 0, None, None)?;
let mut txn = storage.txn()?;
let client = txn.get_client(client_id)?.unwrap();
let parent_version_id = Uuid::nil(); let parent_version_id = Uuid::nil();
let result = add_version( let result = server.add_version(client_id, client, parent_version_id, vec![3, 6, 9])?;
txn,
&ServerConfig::default(),
client_id,
client,
parent_version_id,
vec![3, 6, 9],
)?;
av_success_check( av_success_check(
&storage, &server,
client_id, client_id,
&versions, &versions,
result, result,
@ -656,23 +622,12 @@ mod test {
#[test] #[test]
fn add_version_success_recent_snapshot() -> anyhow::Result<()> { fn add_version_success_recent_snapshot() -> anyhow::Result<()> {
let storage = InMemoryStorage::new(); let (server, client_id, client, versions) = av_setup(1, Some(0), None)?;
let (client_id, versions) = av_setup(&storage, 1, Some(0), None)?;
let mut txn = storage.txn()?; let result = server.add_version(client_id, client, versions[0], vec![1, 2, 3])?;
let client = txn.get_client(client_id)?.unwrap();
let result = add_version(
txn,
&ServerConfig::default(),
client_id,
client,
versions[0],
vec![1, 2, 3],
)?;
av_success_check( av_success_check(
&storage, &server,
client_id, client_id,
&versions, &versions,
result, result,
@ -686,24 +641,13 @@ mod test {
#[test] #[test]
fn add_version_success_aged_snapshot() -> anyhow::Result<()> { fn add_version_success_aged_snapshot() -> anyhow::Result<()> {
let storage = InMemoryStorage::new();
// one snapshot, but it was 50 days ago // one snapshot, but it was 50 days ago
let (client_id, versions) = av_setup(&storage, 1, Some(0), Some(50))?; let (server, client_id, client, versions) = av_setup(1, Some(0), Some(50))?;
let mut txn = storage.txn()?; let result = server.add_version(client_id, client, versions[0], vec![1, 2, 3])?;
let client = txn.get_client(client_id)?.unwrap();
let result = add_version(
txn,
&ServerConfig::default(),
client_id,
client,
versions[0],
vec![1, 2, 3],
)?;
av_success_check( av_success_check(
&storage, &server,
client_id, client_id,
&versions, &versions,
result, result,
@ -717,27 +661,14 @@ mod test {
#[test] #[test]
fn add_version_success_snapshot_many_versions_ago() -> anyhow::Result<()> { fn add_version_success_snapshot_many_versions_ago() -> anyhow::Result<()> {
let storage = InMemoryStorage::new();
// one snapshot, but it was 50 versions ago // one snapshot, but it was 50 versions ago
let (client_id, versions) = av_setup(&storage, 50, Some(0), None)?; let (mut server, client_id, client, versions) = av_setup(50, Some(0), None)?;
server.config.snapshot_versions = 30;
let mut txn = storage.txn()?; let result = server.add_version(client_id, client, versions[49], vec![1, 2, 3])?;
let client = txn.get_client(client_id)?.unwrap();
let result = add_version(
txn,
&ServerConfig {
snapshot_versions: 30,
..ServerConfig::default()
},
client_id,
client,
versions[49],
vec![1, 2, 3],
)?;
av_success_check( av_success_check(
&storage, &server,
client_id, client_id,
&versions, &versions,
result, result,
@ -751,10 +682,7 @@ mod test {
#[test] #[test]
fn add_snapshot_success_latest() -> anyhow::Result<()> { fn add_snapshot_success_latest() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, version_id)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
@ -764,17 +692,12 @@ mod test {
// add a snapshot for that version // add a snapshot for that version
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
add_snapshot( Ok((client_id, client, version_id))
txn, })?;
&ServerConfig::default(), server.add_snapshot(client_id, client, version_id, vec![1, 2, 3])?;
client_id,
client,
version_id,
vec![1, 2, 3],
)?;
// verify the snapshot // verify the snapshot
let mut txn = storage.txn()?; let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
let snapshot = client.snapshot.unwrap(); let snapshot = client.snapshot.unwrap();
assert_eq!(snapshot.version_id, version_id); assert_eq!(snapshot.version_id, version_id);
@ -789,10 +712,7 @@ mod test {
#[test] #[test]
fn add_snapshot_success_older() -> anyhow::Result<()> { fn add_snapshot_success_older() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, version_id_1)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id_1 = Uuid::new_v4(); let version_id_1 = Uuid::new_v4();
let version_id_2 = Uuid::new_v4(); let version_id_2 = Uuid::new_v4();
@ -802,19 +722,14 @@ mod test {
txn.add_version(client_id, version_id_1, NIL_VERSION_ID, vec![])?; txn.add_version(client_id, version_id_1, NIL_VERSION_ID, vec![])?;
txn.add_version(client_id, version_id_2, version_id_1, vec![])?; txn.add_version(client_id, version_id_2, version_id_1, vec![])?;
// add a snapshot for version 1
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
add_snapshot( Ok((client_id, client, version_id_1))
txn, })?;
&ServerConfig::default(), // add a snapshot for version 1
client_id, server.add_snapshot(client_id, client, version_id_1, vec![1, 2, 3])?;
client,
version_id_1,
vec![1, 2, 3],
)?;
// verify the snapshot // verify the snapshot
let mut txn = storage.txn()?; let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
let snapshot = client.snapshot.unwrap(); let snapshot = client.snapshot.unwrap();
assert_eq!(snapshot.version_id, version_id_1); assert_eq!(snapshot.version_id, version_id_1);
@ -829,10 +744,7 @@ mod test {
#[test] #[test]
fn add_snapshot_fails_no_such() -> anyhow::Result<()> { fn add_snapshot_fails_no_such() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id_1 = Uuid::new_v4(); let version_id_1 = Uuid::new_v4();
let version_id_2 = Uuid::new_v4(); let version_id_2 = Uuid::new_v4();
@ -844,18 +756,14 @@ mod test {
// add a snapshot for unknown version // add a snapshot for unknown version
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client))
})?;
let version_id_unk = Uuid::new_v4(); let version_id_unk = Uuid::new_v4();
add_snapshot( server.add_snapshot(client_id, client, version_id_unk, vec![1, 2, 3])?;
txn,
&ServerConfig::default(),
client_id,
client,
version_id_unk,
vec![1, 2, 3],
)?;
// verify the snapshot does not exist // verify the snapshot does not exist
let mut txn = storage.txn()?; let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
assert!(client.snapshot.is_none()); assert!(client.snapshot.is_none());
@ -864,10 +772,7 @@ mod test {
#[test] #[test]
fn add_snapshot_fails_too_old() -> anyhow::Result<()> { fn add_snapshot_fails_too_old() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, version_ids)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let mut version_id = Uuid::new_v4(); let mut version_id = Uuid::new_v4();
let mut parent_version_id = Uuid::nil(); let mut parent_version_id = Uuid::nil();
@ -884,17 +789,12 @@ mod test {
// add a snapshot for the earliest of those // add a snapshot for the earliest of those
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
add_snapshot( Ok((client_id, client, version_ids))
txn, })?;
&ServerConfig::default(), server.add_snapshot(client_id, client, version_ids[0], vec![1, 2, 3])?;
client_id,
client,
version_ids[0],
vec![1, 2, 3],
)?;
// verify the snapshot does not exist // verify the snapshot does not exist
let mut txn = storage.txn()?; let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
assert!(client.snapshot.is_none()); assert!(client.snapshot.is_none());
@ -903,17 +803,14 @@ mod test {
#[test] #[test]
fn add_snapshot_fails_newer_exists() -> anyhow::Result<()> { fn add_snapshot_fails_newer_exists() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, version_ids)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let mut version_id = Uuid::new_v4(); let mut version_id = Uuid::new_v4();
let mut parent_version_id = Uuid::nil(); let mut parent_version_id = Uuid::nil();
let mut version_ids = vec![]; let mut version_ids = vec![];
// set up a task DB with 5 versions in it (oldest to newest) and a snapshot of the middle // set up a task DB with 5 versions in it (oldest to newest) and a snapshot of the
// one // middle one
txn.new_client(client_id, Uuid::nil())?; txn.new_client(client_id, Uuid::nil())?;
for _ in 0..5 { for _ in 0..5 {
txn.add_version(client_id, version_id, parent_version_id, vec![])?; txn.add_version(client_id, version_id, parent_version_id, vec![])?;
@ -933,17 +830,13 @@ mod test {
// add a snapshot for the earliest of those // add a snapshot for the earliest of those
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
add_snapshot( Ok((client_id, client, version_ids))
txn, })?;
&ServerConfig::default(),
client_id, server.add_snapshot(client_id, client, version_ids[0], vec![9, 9, 9])?;
client,
version_ids[0],
vec![9, 9, 9],
)?;
// verify the snapshot was not replaced // verify the snapshot was not replaced
let mut txn = storage.txn()?; let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
let snapshot = client.snapshot.unwrap(); let snapshot = client.snapshot.unwrap();
assert_eq!(snapshot.version_id, version_ids[2]); assert_eq!(snapshot.version_id, version_ids[2]);
@ -958,10 +851,7 @@ mod test {
#[test] #[test]
fn add_snapshot_fails_nil_version() -> anyhow::Result<()> { fn add_snapshot_fails_nil_version() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
// just set up the client // just set up the client
@ -969,17 +859,13 @@ mod test {
// add a snapshot for the nil version // add a snapshot for the nil version
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
add_snapshot( Ok((client_id, client))
txn, })?;
&ServerConfig::default(),
client_id, server.add_snapshot(client_id, client, NIL_VERSION_ID, vec![9, 9, 9])?;
client,
NIL_VERSION_ID,
vec![9, 9, 9],
)?;
// verify the snapshot does not exist // verify the snapshot does not exist
let mut txn = storage.txn()?; let mut txn = server.txn()?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
assert!(client.snapshot.is_none()); assert!(client.snapshot.is_none());
@ -988,10 +874,7 @@ mod test {
#[test] #[test]
fn get_snapshot_found() -> anyhow::Result<()> { fn get_snapshot_found() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client, data, snapshot_version_id)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let data = vec![1, 2, 3]; let data = vec![1, 2, 3];
let snapshot_version_id = Uuid::new_v4(); let snapshot_version_id = Uuid::new_v4();
@ -1006,10 +889,11 @@ mod test {
}, },
data.clone(), data.clone(),
)?; )?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client, data, snapshot_version_id))
})?;
assert_eq!( assert_eq!(
get_snapshot(txn, &ServerConfig::default(), client_id, client)?, server.get_snapshot(client_id, client)?,
Some((snapshot_version_id, data)) Some((snapshot_version_id, data))
); );
@ -1018,19 +902,14 @@ mod test {
#[test] #[test]
fn get_snapshot_not_found() -> anyhow::Result<()> { fn get_snapshot_not_found() -> anyhow::Result<()> {
init_logging(); let (server, (client_id, client)) = setup(|txn| {
let storage = InMemoryStorage::new();
let mut txn = storage.txn()?;
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
txn.new_client(client_id, NIL_VERSION_ID)?; txn.new_client(client_id, NIL_VERSION_ID)?;
let client = txn.get_client(client_id)?.unwrap(); let client = txn.get_client(client_id)?.unwrap();
Ok((client_id, client))
})?;
assert_eq!( assert_eq!(server.get_snapshot(client_id, client)?, None);
get_snapshot(txn, &ServerConfig::default(), client_id, client)?,
None
);
Ok(()) Ok(())
} }

View file

@ -2,7 +2,7 @@ use crate::api::{client_id_header, failure_to_ise, ServerState, SNAPSHOT_CONTENT
use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Result}; use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Result};
use futures::StreamExt; use futures::StreamExt;
use std::sync::Arc; use std::sync::Arc;
use taskchampion_sync_server_core::{add_snapshot, VersionId, NIL_VERSION_ID}; use taskchampion_sync_server_core::{VersionId, NIL_VERSION_ID};
/// Max snapshot size: 100MB /// Max snapshot size: 100MB
const MAX_SIZE: usize = 100 * 1024 * 1024; const MAX_SIZE: usize = 100 * 1024 * 1024;
@ -49,26 +49,23 @@ pub(crate) async fn service(
// note that we do not open the transaction until the body has been read // note that we do not open the transaction until the body has been read
// completely, to avoid blocking other storage access while that data is // completely, to avoid blocking other storage access while that data is
// in transit. // in transit.
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?; let client = {
let mut txn = server_state.server.txn().map_err(failure_to_ise)?;
// get, or create, the client // get, or create, the client
let client = match txn.get_client(client_id).map_err(failure_to_ise)? { match txn.get_client(client_id).map_err(failure_to_ise)? {
Some(client) => client, Some(client) => client,
None => { None => {
txn.new_client(client_id, NIL_VERSION_ID) txn.new_client(client_id, NIL_VERSION_ID)
.map_err(failure_to_ise)?; .map_err(failure_to_ise)?;
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap() txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
} }
}
}; };
add_snapshot( server_state
txn, .server
&server_state.config, .add_snapshot(client_id, client, version_id, body.to_vec())
client_id,
client,
version_id,
body.to_vec(),
)
.map_err(failure_to_ise)?; .map_err(failure_to_ise)?;
Ok(HttpResponse::Ok().body("")) Ok(HttpResponse::Ok().body(""))
} }
@ -77,7 +74,7 @@ pub(crate) async fn service(
mod test { mod test {
use super::*; use super::*;
use crate::api::CLIENT_ID_HEADER; use crate::api::CLIENT_ID_HEADER;
use crate::Server; use crate::WebServer;
use actix_web::{http::StatusCode, test, App}; use actix_web::{http::StatusCode, test, App};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use taskchampion_sync_server_core::{InMemoryStorage, Storage}; use taskchampion_sync_server_core::{InMemoryStorage, Storage};
@ -87,7 +84,7 @@ mod test {
async fn test_success() -> anyhow::Result<()> { async fn test_success() -> anyhow::Result<()> {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -96,7 +93,7 @@ mod test {
txn.add_version(client_id, version_id, NIL_VERSION_ID, vec![])?; txn.add_version(client_id, version_id, NIL_VERSION_ID, vec![])?;
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -130,7 +127,7 @@ mod test {
async fn test_not_added_200() { async fn test_not_added_200() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -138,7 +135,7 @@ mod test {
txn.new_client(client_id, NIL_VERSION_ID).unwrap(); txn.new_client(client_id, NIL_VERSION_ID).unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -167,8 +164,8 @@ mod test {
async fn test_bad_content_type() { async fn test_bad_content_type() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -187,8 +184,8 @@ mod test {
async fn test_empty_body() { async fn test_empty_body() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;

View file

@ -5,9 +5,7 @@ use crate::api::{
use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Result}; use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Result};
use futures::StreamExt; use futures::StreamExt;
use std::sync::Arc; use std::sync::Arc;
use taskchampion_sync_server_core::{ use taskchampion_sync_server_core::{AddVersionResult, SnapshotUrgency, VersionId, NIL_VERSION_ID};
add_version, AddVersionResult, SnapshotUrgency, VersionId, NIL_VERSION_ID,
};
/// Max history segment size: 100MB /// Max history segment size: 100MB
const MAX_SIZE: usize = 100 * 1024 * 1024; const MAX_SIZE: usize = 100 * 1024 * 1024;
@ -59,26 +57,22 @@ pub(crate) async fn service(
// note that we do not open the transaction until the body has been read // note that we do not open the transaction until the body has been read
// completely, to avoid blocking other storage access while that data is // completely, to avoid blocking other storage access while that data is
// in transit. // in transit.
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?; let client = {
let mut txn = server_state.server.txn().map_err(failure_to_ise)?;
// get, or create, the client match txn.get_client(client_id).map_err(failure_to_ise)? {
let client = match txn.get_client(client_id).map_err(failure_to_ise)? {
Some(client) => client, Some(client) => client,
None => { None => {
txn.new_client(client_id, NIL_VERSION_ID) txn.new_client(client_id, NIL_VERSION_ID)
.map_err(failure_to_ise)?; .map_err(failure_to_ise)?;
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap() txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
} }
}
}; };
let (result, snap_urgency) = add_version( let (result, snap_urgency) = server_state
txn, .server
&server_state.config, .add_version(client_id, client, parent_version_id, body.to_vec())
client_id,
client,
parent_version_id,
body.to_vec(),
)
.map_err(failure_to_ise)?; .map_err(failure_to_ise)?;
Ok(match result { Ok(match result {
@ -107,7 +101,7 @@ pub(crate) async fn service(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::api::CLIENT_ID_HEADER; use crate::api::CLIENT_ID_HEADER;
use crate::Server; use crate::WebServer;
use actix_web::{http::StatusCode, test, App}; use actix_web::{http::StatusCode, test, App};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use taskchampion_sync_server_core::{InMemoryStorage, Storage}; use taskchampion_sync_server_core::{InMemoryStorage, Storage};
@ -118,7 +112,7 @@ mod test {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -126,7 +120,7 @@ mod test {
txn.new_client(client_id, Uuid::nil()).unwrap(); txn.new_client(client_id, Uuid::nil()).unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -160,7 +154,7 @@ mod test {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -168,7 +162,7 @@ mod test {
txn.new_client(client_id, version_id).unwrap(); txn.new_client(client_id, version_id).unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -195,8 +189,8 @@ mod test {
async fn test_bad_content_type() { async fn test_bad_content_type() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -215,8 +209,8 @@ mod test {
async fn test_empty_body() { async fn test_empty_body() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;

View file

@ -4,7 +4,7 @@ use crate::api::{
}; };
use actix_web::{error, get, web, HttpRequest, HttpResponse, Result}; use actix_web::{error, get, web, HttpRequest, HttpResponse, Result};
use std::sync::Arc; use std::sync::Arc;
use taskchampion_sync_server_core::{get_child_version, GetVersionResult, VersionId}; use taskchampion_sync_server_core::{GetVersionResult, VersionId};
/// Get a child version. /// Get a child version.
/// ///
@ -22,7 +22,8 @@ pub(crate) async fn service(
) -> Result<HttpResponse> { ) -> Result<HttpResponse> {
let parent_version_id = path.into_inner(); let parent_version_id = path.into_inner();
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?; let (client, client_id) = {
let mut txn = server_state.server.txn().map_err(failure_to_ise)?;
let client_id = client_id_header(&req)?; let client_id = client_id_header(&req)?;
@ -30,14 +31,12 @@ pub(crate) async fn service(
.get_client(client_id) .get_client(client_id)
.map_err(failure_to_ise)? .map_err(failure_to_ise)?
.ok_or_else(|| error::ErrorNotFound("no such client"))?; .ok_or_else(|| error::ErrorNotFound("no such client"))?;
(client, client_id)
};
return match get_child_version( return match server_state
txn, .server
&server_state.config, .get_child_version(client_id, client, parent_version_id)
client_id,
client,
parent_version_id,
)
.map_err(failure_to_ise)? .map_err(failure_to_ise)?
{ {
GetVersionResult::Success { GetVersionResult::Success {
@ -57,7 +56,7 @@ pub(crate) async fn service(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::api::CLIENT_ID_HEADER; use crate::api::CLIENT_ID_HEADER;
use crate::Server; use crate::WebServer;
use actix_web::{http::StatusCode, test, App}; use actix_web::{http::StatusCode, test, App};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use taskchampion_sync_server_core::{InMemoryStorage, Storage, NIL_VERSION_ID}; use taskchampion_sync_server_core::{InMemoryStorage, Storage, NIL_VERSION_ID};
@ -68,7 +67,7 @@ mod test {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -78,7 +77,7 @@ mod test {
.unwrap(); .unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -111,8 +110,8 @@ mod test {
async fn test_client_not_found() { async fn test_client_not_found() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let parent_version_id = Uuid::new_v4(); let parent_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -131,7 +130,7 @@ mod test {
async fn test_version_not_found_and_gone() { async fn test_version_not_found_and_gone() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let test_version_id = Uuid::new_v4(); let test_version_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// create the client and a single version. // create the client and a single version.
{ {
@ -140,7 +139,7 @@ mod test {
txn.add_version(client_id, test_version_id, NIL_VERSION_ID, b"vers".to_vec()) txn.add_version(client_id, test_version_id, NIL_VERSION_ID, b"vers".to_vec())
.unwrap(); .unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;

View file

@ -3,7 +3,6 @@ use crate::api::{
}; };
use actix_web::{error, get, web, HttpRequest, HttpResponse, Result}; use actix_web::{error, get, web, HttpRequest, HttpResponse, Result};
use std::sync::Arc; use std::sync::Arc;
use taskchampion_sync_server_core::get_snapshot;
/// Get a snapshot. /// Get a snapshot.
/// ///
@ -18,17 +17,19 @@ pub(crate) async fn service(
req: HttpRequest, req: HttpRequest,
server_state: web::Data<Arc<ServerState>>, server_state: web::Data<Arc<ServerState>>,
) -> Result<HttpResponse> { ) -> Result<HttpResponse> {
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
let client_id = client_id_header(&req)?; let client_id = client_id_header(&req)?;
let client = txn let client = {
.get_client(client_id) let mut txn = server_state.server.txn().map_err(failure_to_ise)?;
txn.get_client(client_id)
.map_err(failure_to_ise)? .map_err(failure_to_ise)?
.ok_or_else(|| error::ErrorNotFound("no such client"))?; .ok_or_else(|| error::ErrorNotFound("no such client"))?
};
if let Some((version_id, data)) = if let Some((version_id, data)) = server_state
get_snapshot(txn, &server_state.config, client_id, client).map_err(failure_to_ise)? .server
.get_snapshot(client_id, client)
.map_err(failure_to_ise)?
{ {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
.content_type(SNAPSHOT_CONTENT_TYPE) .content_type(SNAPSHOT_CONTENT_TYPE)
@ -42,7 +43,7 @@ pub(crate) async fn service(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::api::CLIENT_ID_HEADER; use crate::api::CLIENT_ID_HEADER;
use crate::Server; use crate::WebServer;
use actix_web::{http::StatusCode, test, App}; use actix_web::{http::StatusCode, test, App};
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -52,7 +53,7 @@ mod test {
#[actix_rt::test] #[actix_rt::test]
async fn test_not_found() { async fn test_not_found() {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -60,7 +61,7 @@ mod test {
txn.new_client(client_id, Uuid::new_v4()).unwrap(); txn.new_client(client_id, Uuid::new_v4()).unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;
@ -78,7 +79,7 @@ mod test {
let client_id = Uuid::new_v4(); let client_id = Uuid::new_v4();
let version_id = Uuid::new_v4(); let version_id = Uuid::new_v4();
let snapshot_data = vec![1, 2, 3, 4]; let snapshot_data = vec![1, 2, 3, 4];
let storage: Box<dyn Storage> = Box::new(InMemoryStorage::new()); let storage = InMemoryStorage::new();
// set up the storage contents.. // set up the storage contents..
{ {
@ -96,7 +97,7 @@ mod test {
.unwrap(); .unwrap();
} }
let server = Server::new(Default::default(), storage); let server = WebServer::new(Default::default(), storage);
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;

View file

@ -1,5 +1,5 @@
use actix_web::{error, http::StatusCode, web, HttpRequest, Result, Scope}; use actix_web::{error, http::StatusCode, web, HttpRequest, Result, Scope};
use taskchampion_sync_server_core::{ClientId, ServerConfig, Storage}; use taskchampion_sync_server_core::{ClientId, Server};
mod add_snapshot; mod add_snapshot;
mod add_version; mod add_version;
@ -27,8 +27,7 @@ pub(crate) const SNAPSHOT_REQUEST_HEADER: &str = "X-Snapshot-Request";
/// The type containing a reference to the persistent state for the server /// The type containing a reference to the persistent state for the server
pub(crate) struct ServerState { pub(crate) struct ServerState {
pub(crate) storage: Box<dyn Storage>, pub(crate) server: Server,
pub(crate) config: ServerConfig,
} }
pub(crate) fn api_scope() -> Scope { pub(crate) fn api_scope() -> Scope {

View file

@ -3,7 +3,7 @@
use actix_web::{middleware::Logger, App, HttpServer}; use actix_web::{middleware::Logger, App, HttpServer};
use clap::{arg, builder::ValueParser, value_parser, Command}; use clap::{arg, builder::ValueParser, value_parser, Command};
use std::ffi::OsString; use std::ffi::OsString;
use taskchampion_sync_server::Server; use taskchampion_sync_server::WebServer;
use taskchampion_sync_server_core::ServerConfig; use taskchampion_sync_server_core::ServerConfig;
use taskchampion_sync_server_storage_sqlite::SqliteStorage; use taskchampion_sync_server_storage_sqlite::SqliteStorage;
@ -44,8 +44,11 @@ async fn main() -> anyhow::Result<()> {
let snapshot_versions: u32 = *matches.get_one("snapshot-versions").unwrap(); let snapshot_versions: u32 = *matches.get_one("snapshot-versions").unwrap();
let snapshot_days: i64 = *matches.get_one("snapshot-days").unwrap(); let snapshot_days: i64 = *matches.get_one("snapshot-days").unwrap();
let config = ServerConfig::from_args(snapshot_days, snapshot_versions)?; let config = ServerConfig {
let server = Server::new(config, Box::new(SqliteStorage::new(data_dir)?)); snapshot_days,
snapshot_versions,
};
let server = WebServer::new(config, SqliteStorage::new(data_dir)?);
log::info!("Serving on port {}", port); log::info!("Serving on port {}", port);
HttpServer::new(move || { HttpServer::new(move || {
@ -67,7 +70,7 @@ mod test {
#[actix_rt::test] #[actix_rt::test]
async fn test_index_get() { async fn test_index_get() {
let server = Server::new(Default::default(), Box::new(InMemoryStorage::new())); let server = WebServer::new(Default::default(), InMemoryStorage::new());
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;

View file

@ -5,7 +5,7 @@ mod api;
use actix_web::{get, middleware, web, Responder}; use actix_web::{get, middleware, web, Responder};
use api::{api_scope, ServerState}; use api::{api_scope, ServerState};
use std::sync::Arc; use std::sync::Arc;
use taskchampion_sync_server_core::{ServerConfig, Storage}; use taskchampion_sync_server_core::{Server, ServerConfig, Storage};
#[get("/")] #[get("/")]
async fn index() -> impl Responder { async fn index() -> impl Responder {
@ -14,15 +14,17 @@ async fn index() -> impl Responder {
/// A Server represents a sync server. /// A Server represents a sync server.
#[derive(Clone)] #[derive(Clone)]
pub struct Server { pub struct WebServer {
server_state: Arc<ServerState>, server_state: Arc<ServerState>,
} }
impl Server { impl WebServer {
/// Create a new sync server with the given storage implementation. /// Create a new sync server with the given storage implementation.
pub fn new(config: ServerConfig, storage: Box<dyn Storage>) -> Self { pub fn new<ST: Storage + 'static>(config: ServerConfig, storage: ST) -> Self {
Self { Self {
server_state: Arc::new(ServerState { config, storage }), server_state: Arc::new(ServerState {
server: Server::new(config, storage),
}),
} }
} }
@ -49,7 +51,7 @@ mod test {
#[actix_rt::test] #[actix_rt::test]
async fn test_cache_control() { async fn test_cache_control() {
let server = Server::new(Default::default(), Box::new(InMemoryStorage::new())); let server = WebServer::new(Default::default(), InMemoryStorage::new());
let app = App::new().configure(|sc| server.config(sc)); let app = App::new().configure(|sc| server.config(sc));
let app = test::init_service(app).await; let app = test::init_service(app).await;

File diff suppressed because it is too large Load diff