refactor sync server into modules

This commit is contained in:
Dustin J. Mitchell 2020-11-25 23:16:05 -05:00
parent d0bfbbb7f0
commit 087333a227
9 changed files with 1504 additions and 102 deletions

1388
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,3 +7,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-web = "3.3.0"
failure = "0.1.8"
serde = "1.0.117"
serde_json = "1.0.59"
taskchampion = { path = "../taskchampion" }

View file

@ -0,0 +1,25 @@
use crate::server::SyncServer;
use crate::types::{ClientId, HistorySegment, VersionId};
use actix_web::{error, http::StatusCode, post, web, HttpResponse, Responder, Result};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
/// Request body to add_version
#[derive(Serialize, Deserialize)]
pub(crate) struct AddVersionRequest {
// TODO: temporary!
#[serde(default)]
history_segment: HistorySegment,
}
#[post("/client/{client_id}/add-version/{parent_version_id}")]
pub(crate) async fn service(
data: web::Data<Arc<SyncServer>>,
web::Path((client_id, parent_version_id)): web::Path<(ClientId, VersionId)>,
body: web::Json<AddVersionRequest>,
) -> Result<impl Responder> {
let result = data
.add_version(client_id, parent_version_id, &body.history_segment)
.map_err(|e| error::InternalError::new(e, StatusCode::INTERNAL_SERVER_ERROR))?;
Ok(HttpResponse::Ok().json(result))
}

View file

@ -0,0 +1,19 @@
use crate::server::SyncServer;
use crate::types::{ClientId, VersionId};
use actix_web::{error, get, http::StatusCode, web, HttpResponse, Result};
use std::sync::Arc;
#[get("/client/{client_id}/get-child-version/{parent_version_id}")]
pub(crate) async fn service(
data: web::Data<Arc<SyncServer>>,
web::Path((client_id, parent_version_id)): web::Path<(ClientId, VersionId)>,
) -> Result<HttpResponse> {
let result = data
.get_child_version(client_id, parent_version_id)
.map_err(|e| error::InternalError::new(e, StatusCode::INTERNAL_SERVER_ERROR))?;
if let Some(result) = result {
Ok(HttpResponse::Ok().json(result))
} else {
Err(error::ErrorNotFound("no such version"))
}
}

View file

@ -0,0 +1,2 @@
pub(crate) mod add_version;
pub(crate) mod get_child_version;

View file

@ -1,86 +0,0 @@
#![allow(clippy::new_without_default)]
use std::collections::HashMap;
type Blob = Vec<u8>;
struct User {
// versions, indexed at v-1
versions: Vec<Blob>,
snapshots: HashMap<u64, Blob>,
}
pub struct Server {
users: HashMap<String, User>,
}
pub enum VersionAdd {
// OK, version added
Ok,
// Rejected, must be based on the the given version
ExpectedVersion(u64),
}
impl User {
fn new() -> User {
User {
versions: vec![],
snapshots: HashMap::new(),
}
}
fn get_versions(&self, since_version: u64) -> Vec<Blob> {
let last_version = self.versions.len();
if last_version == since_version as usize {
return vec![];
}
self.versions[since_version as usize..last_version].to_vec()
}
fn add_version(&mut self, version: u64, blob: Blob) -> VersionAdd {
// of by one here: client wants to send version 1 first
let expected_version = self.versions.len() as u64 + 1;
if version != expected_version {
return VersionAdd::ExpectedVersion(expected_version);
}
self.versions.push(blob);
VersionAdd::Ok
}
fn add_snapshot(&mut self, version: u64, blob: Blob) {
self.snapshots.insert(version, blob);
}
}
impl Server {
pub fn new() -> Server {
Server {
users: HashMap::new(),
}
}
fn get_user_mut(&mut self, username: &str) -> &mut User {
self.users
.entry(username.to_string())
.or_insert_with(User::new)
}
/// Get a vector of all versions after `since_version`
pub fn get_versions(&self, username: &str, since_version: u64) -> Vec<Blob> {
self.users
.get(username)
.map(|user| user.get_versions(since_version))
.unwrap_or_default()
}
/// Add a new version. If the given version number is incorrect, this responds with the
/// appropriate version and expects the caller to try again.
pub fn add_version(&mut self, username: &str, version: u64, blob: Blob) -> VersionAdd {
self.get_user_mut(username).add_version(version, blob)
}
pub fn add_snapshot(&mut self, username: &str, version: u64, blob: Blob) {
self.get_user_mut(username).add_snapshot(version, blob);
}
}

24
sync-server/src/main.rs Normal file
View file

@ -0,0 +1,24 @@
use actix_web::{App, HttpServer};
use server::SyncServer;
use std::sync::Arc;
mod api;
mod server;
mod types;
// TODO: use hawk to sign requests
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let sync_server = Arc::new(SyncServer::new());
HttpServer::new(move || {
App::new()
.data(sync_server.clone())
.service(api::get_child_version::service)
.service(api::add_version::service)
})
.bind("127.0.0.1:8080")?
.run()
.await
}

34
sync-server/src/server.rs Normal file
View file

@ -0,0 +1,34 @@
use crate::types::{AddVersionResult, ClientId, GetVersionResult, HistorySegment, VersionId};
use failure::Fallible;
use taskchampion::Uuid;
/// The sync server's implementation; HTTP API method call through to methods on a single
/// instance of this type.
pub(crate) struct SyncServer {}
impl SyncServer {
pub(crate) fn new() -> Self {
Self {}
}
pub(crate) fn get_child_version(
&self,
_client_id: ClientId,
parent_version_id: VersionId,
) -> Fallible<Option<GetVersionResult>> {
Ok(Some(GetVersionResult {
version_id: Uuid::new_v4(),
parent_version_id,
history_segment: b"abcd".to_vec(),
}))
}
pub(crate) fn add_version(
&self,
_client_id: ClientId,
_parent_version_id: VersionId,
_history_segment: &HistorySegment,
) -> Fallible<AddVersionResult> {
Ok(AddVersionResult::Ok(Uuid::new_v4()))
}
}

23
sync-server/src/types.rs Normal file
View file

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
use taskchampion::Uuid;
pub(crate) type HistorySegment = Vec<u8>;
pub(crate) type ClientId = Uuid;
pub(crate) type VersionId = Uuid;
/// Response to get_child_version
#[derive(Serialize, Deserialize)]
pub(crate) struct GetVersionResult {
pub(crate) version_id: Uuid,
pub(crate) parent_version_id: Uuid,
pub(crate) history_segment: HistorySegment,
}
/// Response to add_version
#[derive(Serialize, Deserialize)]
pub(crate) enum AddVersionResult {
/// OK, version added with the given ID
Ok(VersionId),
/// Rejected; expected a version with the given parent version
ExpectedParentVersion(VersionId),
}