api: item/sync: fix cursor_token and sync_token logic

This commit is contained in:
Peter Cai 2020-02-21 17:27:05 +08:00
parent fd9b1310b0
commit 5d5e88fcef
No known key found for this signature in database
GPG key ID: 71F5FB4E4F3FD54F
2 changed files with 33 additions and 16 deletions

View file

@ -165,8 +165,8 @@ fn items_sync(db: DbConn, u: user::User, params: Json<SyncParams>) -> Custom<Jso
retrieved_items: vec![], retrieved_items: vec![],
saved_items: vec![], saved_items: vec![],
unsaved: vec![], unsaved: vec![],
sync_token: params.sync_token.clone(), sync_token: None,
cursor_token: params.cursor_token.clone() cursor_token: None
}; };
let inner_params = params.into_inner(); let inner_params = params.into_inner();
@ -189,16 +189,15 @@ fn items_sync(db: DbConn, u: user::User, params: Json<SyncParams>) -> Custom<Jso
} }
let mut from_id: Option<i64> = None; let mut from_id: Option<i64> = None;
let mut max_id: Option<i64> = None; let mut had_cursor = false;
if let Some(cursor_token) = inner_params.cursor_token { if let Some(cursor_token) = inner_params.cursor_token {
// If the client provides cursor_token, // If the client provides cursor_token,
// then, we return all records // then, we return all records
// until sync_token (the head of the last sync) // until sync_token (the head of the last sync)
from_id = cursor_token.parse().ok(); from_id = cursor_token.parse().ok();
max_id = inner_params.sync_token.clone() had_cursor = true;
.and_then(|i| i.parse().ok()); } else if let Some(sync_token) = inner_params.sync_token.clone() {
} else if let Some(sync_token) = inner_params.sync_token {
// If there is no cursor_token, then we are doing // If there is no cursor_token, then we are doing
// a normal sync, so just return all records from sync_token // a normal sync, so just return all records from sync_token
from_id = sync_token.parse().ok(); from_id = sync_token.parse().ok();
@ -206,7 +205,7 @@ fn items_sync(db: DbConn, u: user::User, params: Json<SyncParams>) -> Custom<Jso
// Then, retrieve what the client needs // Then, retrieve what the client needs
let result = item::SyncItem::items_of_user(&db, &u, let result = item::SyncItem::items_of_user(&db, &u,
from_id, max_id, inner_params.limit); from_id, None, inner_params.limit);
match result { match result {
Err(item::ItemOpError(e)) => { Err(item::ItemOpError(e)) => {
@ -214,15 +213,33 @@ fn items_sync(db: DbConn, u: user::User, params: Json<SyncParams>) -> Custom<Jso
}, },
Ok(items) => { Ok(items) => {
if !items.is_empty() { if !items.is_empty() {
// max_id = the last sync token // If we fetched something
// if we still haven't reached the last sync token yet, // we should record the current last id to *some* token
// return a new cursor token and keep the sync token // this may be `cursor_token` or `sync_token`
if let Some(max_id) = max_id { // if we have more to fetch, we set `cursor_token` to this id
resp.cursor_token = Some(items[0].id.to_string()); // so that the client knows to continue
resp.sync_token = Some(max_id.to_string()); // and we set `sync_token` to this id regardless, for the
// client to remember where we were even if
// it doesn't need to continue
let next_from = items.last().unwrap().id;
if let Some(limit) = inner_params.limit {
if items.len() as i64 == limit {
// We may still have something to fetch
resp.cursor_token = Some(next_from.to_string());
}
}
// Always set sync_token to equal cursor_token in this case
resp.sync_token = Some(next_from.to_string());
} else {
if had_cursor {
// If we already have no item to give, but the client still holds a cursor
// Revoke that cursor, and make it the sync_token
resp.sync_token = resp.cursor_token.clone();
resp.cursor_token = None;
} else { } else {
// Else, use the current max id as the sync_token // Pass the same sync_token back
resp.sync_token = Some(items[0].id.to_string()); resp.sync_token = inner_params.sync_token.clone();
} }
} }
resp.retrieved_items = items.into_iter().map(|x| x.into()).collect(); resp.retrieved_items = items.into_iter().map(|x| x.into()).collect();

View file

@ -95,7 +95,7 @@ impl SyncItem {
stmt = stmt.filter(id.le(max_id)); stmt = stmt.filter(id.le(max_id));
} }
stmt.order(id.desc()) stmt.order(id.asc())
.load::<Item>(db) .load::<Item>(db)
.map_err(|_| "Database error".into()) .map_err(|_| "Database error".into())
}) })