diff --git a/src/api.rs b/src/api.rs index 9d5afc0..60d50af 100644 --- a/src/api.rs +++ b/src/api.rs @@ -150,11 +150,31 @@ struct SyncParams { limit: Option } +#[derive(Serialize)] +struct SyncConflict { + #[serde(rename(serialize = "type"))] + conf_type: String, + server_item: Option, + unsaved_item: Option +} + +impl SyncConflict { + fn uuid<'a>(&self) -> String { + if let Some(ref item) = self.server_item { + item.uuid.clone() + } else if let Some(ref item) = self.unsaved_item { + item.uuid.clone() + } else { + panic!("SyncConflict should have either server_item or unsaved_item"); + } + } +} + #[derive(Serialize)] struct SyncResp { retrieved_items: Vec, saved_items: Vec, - unsaved: Vec, + conflicts: Vec, sync_token: Option, // for convenience, we will actually always return this cursor_token: Option } @@ -164,27 +184,13 @@ fn items_sync(db: DbConn, u: user::User, params: Json) -> Custom = None; let mut had_cursor = false; @@ -200,13 +206,13 @@ fn items_sync(db: DbConn, u: user::User, params: Json) -> Custom { - error_resp(Status::InternalServerError, vec![e]) + return error_resp(Status::InternalServerError, vec![e]) }, Ok(items) => { if !items.is_empty() { @@ -226,21 +232,93 @@ fn items_sync(db: DbConn, u: user::User, params: Json) -> Custom { + return error_resp(Status::InternalServerError, vec![e]); + }, + Ok(id) => { + last_id = id; + resp.saved_items.push(it); + } + } + } + + if last_id > -1 { + // Update sync_token to the latest one of our saved items + resp.sync_token = Some(last_id.to_string()); + } + + // Remove conflicted items from retrieved items + let mut new_retrieved = vec![]; + for x in resp.retrieved_items.into_iter() { + let mut is_conflict = false; + for y in resp.conflicts.iter() { + if x.uuid == y.uuid() { + is_conflict = true; + } + } + + if !is_conflict { + new_retrieved.push(x); + } + } + resp.retrieved_items = new_retrieved; + + Custom(Status::Ok, Json(Response::Success(resp))) } \ No newline at end of file diff --git a/src/item.rs b/src/item.rs index 2d8fe99..6168b60 100644 --- a/src/item.rs +++ b/src/item.rs @@ -48,7 +48,7 @@ struct InsertItem { updated_at: Option } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct SyncItem { pub uuid: String, pub content: Option, @@ -101,7 +101,16 @@ impl SyncItem { }) } - pub fn items_insert(db: &SqliteConnection, u: &user::User, it: &SyncItem) -> Result<(), ItemOpError> { + pub fn find_item_by_uuid(db: &SqliteConnection, u: &user::User, i: &str) -> Result { + lock_db_read!() + .and_then(|_| { + items.filter(owner.eq(u.id).and(uuid.eq(i))) + .first::(db) + .map_err(|_| "Database error".into()) + }) + } + + pub fn items_insert(db: &SqliteConnection, u: &user::User, it: &SyncItem) -> Result { // First, try to find the original item, if any, delete it, and insert a new one with the same UUID // This way, the ID is updated each time an item is updated // This method acts both as insertion and update @@ -133,7 +142,10 @@ impl SyncItem { updated_at: it.updated_at.clone() }) .execute(db) - .map(|_| ()) - .map_err(|_| "Database error".into()) + .map_err(|_| "Database error".into())?; + std::mem::drop(_lock); + + Self::find_item_by_uuid(db, u, &it.uuid) + .map(|i| i.id) } } \ No newline at end of file