mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Add RemoteServer to the taskchampion crate
This commit is contained in:
parent
d46f20e75a
commit
2a37f090a5
10 changed files with 322 additions and 29 deletions
161
Cargo.lock
generated
161
Cargo.lock
generated
|
@ -541,6 +541,12 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chunked_transfer"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.33.3"
|
||||||
|
@ -597,6 +603,22 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie_store"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3"
|
||||||
|
dependencies = [
|
||||||
|
"cookie",
|
||||||
|
"idna",
|
||||||
|
"log",
|
||||||
|
"publicsuffix",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"time 0.2.23",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "copyless"
|
name = "copyless"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -733,6 +755,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "error-chain"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "failure"
|
name = "failure"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
@ -1074,6 +1105,15 @@ version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel32-sys"
|
name = "kernel32-sys"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -1497,6 +1537,28 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "publicsuffix"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
|
||||||
|
dependencies = [
|
||||||
|
"error-chain",
|
||||||
|
"idna",
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qstring"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
|
@ -1744,6 +1806,21 @@ dependencies = [
|
||||||
"quick-error",
|
"quick-error",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.16.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70017ed5c555d79ee3538fc63ca09c70ad8f317dcadc1adc2c496b60c22bb24f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"spin",
|
||||||
|
"untrusted",
|
||||||
|
"web-sys",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rmp"
|
name = "rmp"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
|
@ -1792,6 +1869,19 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.12.3",
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"sct",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusty-fork"
|
name = "rusty-fork"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -1816,6 +1906,16 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sct"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -1926,6 +2026,12 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "standback"
|
name = "standback"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
@ -2025,6 +2131,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
|
"ureq",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2359,6 +2466,31 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq"
|
||||||
|
version = "1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a599426c7388ab189dfd0eeb84c8d879490abc73e3e62a0b6a40e286f6427ab7"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.0",
|
||||||
|
"chunked_transfer",
|
||||||
|
"cookie",
|
||||||
|
"cookie_store",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"qstring",
|
||||||
|
"rustls",
|
||||||
|
"url",
|
||||||
|
"webpki",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -2468,6 +2600,35 @@ version = "0.2.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
|
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f"
|
||||||
|
dependencies = [
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
|
|
@ -52,8 +52,9 @@ impl CommandInvocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_server(&self) -> Fallible<impl server::Server> {
|
pub(super) fn get_server(&self) -> Fallible<impl server::Server> {
|
||||||
Ok(server::LocalServer::new(Path::new(
|
Ok(server::RemoteServer::new(
|
||||||
"/tmp/task-sync-server",
|
"http://localhost:8080".into(),
|
||||||
))?)
|
Uuid::parse_str("d5b55cbd-9a82-4860-9a39-41b67893b22f").unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::api::{
|
||||||
failure_to_ise, ServerState, HISTORY_SEGMENT_CONTENT_TYPE, PARENT_VERSION_ID_HEADER,
|
failure_to_ise, ServerState, HISTORY_SEGMENT_CONTENT_TYPE, PARENT_VERSION_ID_HEADER,
|
||||||
VERSION_ID_HEADER,
|
VERSION_ID_HEADER,
|
||||||
};
|
};
|
||||||
use crate::server::{add_version, AddVersionResult, ClientId, VersionId};
|
use crate::server::{add_version, AddVersionResult, ClientId, VersionId, NO_VERSION_ID};
|
||||||
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;
|
||||||
|
|
||||||
|
@ -51,10 +51,15 @@ pub(crate) async fn service(
|
||||||
// in transit.
|
// in transit.
|
||||||
let mut txn = server_state.txn().map_err(failure_to_ise)?;
|
let mut txn = server_state.txn().map_err(failure_to_ise)?;
|
||||||
|
|
||||||
let client = txn
|
// get, or create, the client
|
||||||
.get_client(client_id)
|
let client = match txn.get_client(client_id).map_err(failure_to_ise)? {
|
||||||
.map_err(failure_to_ise)?
|
Some(client) => client,
|
||||||
.ok_or_else(|| error::ErrorNotFound("no such client"))?;
|
None => {
|
||||||
|
txn.new_client(client_id, NO_VERSION_ID)
|
||||||
|
.map_err(failure_to_ise)?;
|
||||||
|
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let result = add_version(txn, client_id, client, parent_version_id, body.to_vec())
|
let result = add_version(txn, client_id, client, parent_version_id, body.to_vec())
|
||||||
.map_err(failure_to_ise)?;
|
.map_err(failure_to_ise)?;
|
||||||
|
@ -86,8 +91,7 @@ mod test {
|
||||||
// set up the storage contents..
|
// set up the storage contents..
|
||||||
{
|
{
|
||||||
let mut txn = server_box.txn().unwrap();
|
let mut txn = server_box.txn().unwrap();
|
||||||
txn.set_client_latest_version_id(client_id, Uuid::nil())
|
txn.new_client(client_id, Uuid::nil()).unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let server_state = ServerState::new(server_box);
|
let server_state = ServerState::new(server_box);
|
||||||
|
@ -123,8 +127,7 @@ mod test {
|
||||||
// set up the storage contents..
|
// set up the storage contents..
|
||||||
{
|
{
|
||||||
let mut txn = server_box.txn().unwrap();
|
let mut txn = server_box.txn().unwrap();
|
||||||
txn.set_client_latest_version_id(client_id, version_id)
|
txn.new_client(client_id, version_id).unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let server_state = ServerState::new(server_box);
|
let server_state = ServerState::new(server_box);
|
||||||
|
|
|
@ -57,8 +57,7 @@ mod test {
|
||||||
// set up the storage contents..
|
// set up the storage contents..
|
||||||
{
|
{
|
||||||
let mut txn = server_box.txn().unwrap();
|
let mut txn = server_box.txn().unwrap();
|
||||||
txn.set_client_latest_version_id(client_id, Uuid::new_v4())
|
txn.new_client(client_id, Uuid::new_v4()).unwrap();
|
||||||
.unwrap();
|
|
||||||
txn.add_version(client_id, version_id, parent_version_id, b"abcd".to_vec())
|
txn.add_version(client_id, version_id, parent_version_id, b"abcd".to_vec())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -119,8 +118,7 @@ mod test {
|
||||||
// create the client, but not the version
|
// create the client, but not the version
|
||||||
{
|
{
|
||||||
let mut txn = server_box.txn().unwrap();
|
let mut txn = server_box.txn().unwrap();
|
||||||
txn.set_client_latest_version_id(client_id, Uuid::new_v4())
|
txn.new_client(client_id, Uuid::new_v4()).unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
let server_state = ServerState::new(server_box);
|
let server_state = ServerState::new(server_box);
|
||||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||||
|
|
|
@ -149,14 +149,15 @@ mod test {
|
||||||
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 history_segment = b"abcd".to_vec();
|
let history_segment = b"abcd".to_vec();
|
||||||
let client = Client {
|
let latest_version_id = if latest_version_id_nil {
|
||||||
latest_version_id: if latest_version_id_nil {
|
|
||||||
Uuid::nil()
|
Uuid::nil()
|
||||||
} else {
|
} else {
|
||||||
parent_version_id
|
parent_version_id
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
txn.new_client(client_id, latest_version_id)?;
|
||||||
|
let client = txn.get_client(client_id)?.unwrap();
|
||||||
|
|
||||||
let result = add_version(
|
let result = add_version(
|
||||||
txn,
|
txn,
|
||||||
client_id,
|
client_id,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{Client, Storage, StorageTxn, Uuid, Version};
|
use super::{Client, Storage, StorageTxn, Uuid, Version};
|
||||||
use failure::Fallible;
|
use failure::{format_err, Fallible};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
|
@ -38,19 +38,26 @@ impl<'a> StorageTxn for InnerTxn<'a> {
|
||||||
Ok(self.0.clients.get(&client_id).cloned())
|
Ok(self.0.clients.get(&client_id).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_client(&mut self, client_id: Uuid, latest_version_id: Uuid) -> Fallible<()> {
|
||||||
|
if let Some(_) = self.0.clients.get(&client_id) {
|
||||||
|
return Err(format_err!("Client {} already exists", client_id));
|
||||||
|
}
|
||||||
|
self.0
|
||||||
|
.clients
|
||||||
|
.insert(client_id, Client { latest_version_id });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn set_client_latest_version_id(
|
fn set_client_latest_version_id(
|
||||||
&mut self,
|
&mut self,
|
||||||
client_id: Uuid,
|
client_id: Uuid,
|
||||||
latest_version_id: Uuid,
|
latest_version_id: Uuid,
|
||||||
) -> Fallible<()> {
|
) -> Fallible<()> {
|
||||||
if let Some(client) = self.0.clients.get_mut(&client_id) {
|
if let Some(client) = self.0.clients.get_mut(&client_id) {
|
||||||
client.latest_version_id = latest_version_id;
|
Ok(client.latest_version_id = latest_version_id)
|
||||||
} else {
|
} else {
|
||||||
self.0
|
Err(format_err!("Client {} does not exist", client_id))
|
||||||
.clients
|
|
||||||
.insert(client_id, Client { latest_version_id });
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_version_by_parent(
|
fn get_version_by_parent(
|
||||||
|
|
|
@ -20,7 +20,10 @@ pub(crate) trait StorageTxn {
|
||||||
/// Get information about the given client
|
/// Get information about the given client
|
||||||
fn get_client(&mut self, client_id: Uuid) -> Fallible<Option<Client>>;
|
fn get_client(&mut self, client_id: Uuid) -> Fallible<Option<Client>>;
|
||||||
|
|
||||||
/// Set the client's latest_version_id (creating the client if necessary)
|
/// Create a new client with the given latest_version_id
|
||||||
|
fn new_client(&mut self, client_id: Uuid, latest_version_id: Uuid) -> Fallible<()>;
|
||||||
|
|
||||||
|
/// Set the client's latest_version_id
|
||||||
fn set_client_latest_version_id(
|
fn set_client_latest_version_id(
|
||||||
&mut self,
|
&mut self,
|
||||||
client_id: Uuid,
|
client_id: Uuid,
|
||||||
|
|
|
@ -12,6 +12,7 @@ chrono = { version = "0.4.10", features = ["serde"] }
|
||||||
failure = {version = "0.1.5", features = ["derive"] }
|
failure = {version = "0.1.5", features = ["derive"] }
|
||||||
kv = {version = "0.10.0", features = ["msgpack-value"]}
|
kv = {version = "0.10.0", features = ["msgpack-value"]}
|
||||||
lmdb-rkv = {version = "0.12.3"}
|
lmdb-rkv = {version = "0.12.3"}
|
||||||
|
ureq = "1.5.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
proptest = "0.9.4"
|
proptest = "0.9.4"
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
pub(crate) mod test;
|
pub(crate) mod test;
|
||||||
|
|
||||||
mod local;
|
mod local;
|
||||||
|
mod remote;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use local::LocalServer;
|
pub use local::LocalServer;
|
||||||
|
pub use remote::RemoteServer;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
116
taskchampion/src/server/remote.rs
Normal file
116
taskchampion/src/server/remote.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use crate::server::{AddVersionResult, GetVersionResult, HistorySegment, Server, VersionId};
|
||||||
|
use failure::{format_err, Fallible};
|
||||||
|
use std::io::Read;
|
||||||
|
use ureq;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub struct RemoteServer {
|
||||||
|
origin: String,
|
||||||
|
client_id: Uuid,
|
||||||
|
agent: ureq::Agent,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A RemoeServer communicates with a remote server over HTTP (such as with
|
||||||
|
/// taskchampion-sync-server).
|
||||||
|
impl RemoteServer {
|
||||||
|
/// Construct a new RemoteServer. The `origin` is the sync server's protocol and hostname
|
||||||
|
/// without a trailing slash, such as `https://tcsync.example.com`. Pass a client_id to
|
||||||
|
/// identify this client to the server. Multiple replicas synchronizing the same task history
|
||||||
|
/// should use the same client_id.
|
||||||
|
pub fn new(origin: String, client_id: Uuid) -> RemoteServer {
|
||||||
|
RemoteServer {
|
||||||
|
origin,
|
||||||
|
client_id,
|
||||||
|
agent: ureq::agent(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a ureq::Response to an Error
|
||||||
|
fn resp_to_error(resp: ureq::Response) -> failure::Error {
|
||||||
|
return format_err!(
|
||||||
|
"error {}: {}",
|
||||||
|
resp.status(),
|
||||||
|
resp.into_string()
|
||||||
|
.unwrap_or_else(|e| format!("(could not read response: {})", e))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a UUID-bearing header or fail trying
|
||||||
|
fn get_uuid_header(resp: &ureq::Response, name: &str) -> Fallible<Uuid> {
|
||||||
|
let value = resp
|
||||||
|
.header(name)
|
||||||
|
.ok_or_else(|| format_err!("Response does not have {} header", name))?;
|
||||||
|
let value = Uuid::parse_str(value)
|
||||||
|
.map_err(|e| format_err!("{} header is not a valid UUID: {}", name, e))?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the body of a request as a HistorySegment
|
||||||
|
fn into_body(resp: ureq::Response) -> Fallible<HistorySegment> {
|
||||||
|
if let Some("application/vnd.taskchampion.history-segment") = resp.header("Content-Type") {
|
||||||
|
let mut reader = resp.into_reader();
|
||||||
|
let mut bytes = vec![];
|
||||||
|
reader.read_to_end(&mut bytes)?;
|
||||||
|
Ok(bytes)
|
||||||
|
} else {
|
||||||
|
Err(format_err!("Response did not have expected content-type"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Server for RemoteServer {
|
||||||
|
fn add_version(
|
||||||
|
&mut self,
|
||||||
|
parent_version_id: VersionId,
|
||||||
|
history_segment: HistorySegment,
|
||||||
|
) -> Fallible<AddVersionResult> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/client/{}/add-version/{}",
|
||||||
|
self.origin, self.client_id, parent_version_id
|
||||||
|
);
|
||||||
|
let resp = self
|
||||||
|
.agent
|
||||||
|
.post(&url)
|
||||||
|
.timeout_connect(10_000)
|
||||||
|
.timeout_read(60_000)
|
||||||
|
.set(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.taskchampion.history-segment",
|
||||||
|
)
|
||||||
|
.send_bytes(&history_segment);
|
||||||
|
if resp.ok() {
|
||||||
|
let version_id = get_uuid_header(&resp, "X-Version-Id")?;
|
||||||
|
Ok(AddVersionResult::Ok(version_id))
|
||||||
|
} else if resp.status() == 409 {
|
||||||
|
let parent_version_id = get_uuid_header(&resp, "X-Parent-Version-Id")?;
|
||||||
|
Ok(AddVersionResult::ExpectedParentVersion(parent_version_id))
|
||||||
|
} else {
|
||||||
|
Err(resp_to_error(resp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_child_version(&mut self, parent_version_id: VersionId) -> Fallible<GetVersionResult> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/client/{}/get-child-version/{}",
|
||||||
|
self.origin, self.client_id, parent_version_id
|
||||||
|
);
|
||||||
|
let resp = self
|
||||||
|
.agent
|
||||||
|
.get(&url)
|
||||||
|
.timeout_connect(10_000)
|
||||||
|
.timeout_read(60_000)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
if resp.ok() {
|
||||||
|
Ok(GetVersionResult::Version {
|
||||||
|
version_id: get_uuid_header(&resp, "X-Version-Id")?,
|
||||||
|
parent_version_id: get_uuid_header(&resp, "X-Parent-Version-Id")?,
|
||||||
|
history_segment: into_body(resp)?,
|
||||||
|
})
|
||||||
|
} else if resp.status() == 404 {
|
||||||
|
Ok(GetVersionResult::NoSuchVersion)
|
||||||
|
} else {
|
||||||
|
Err(resp_to_error(resp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue