From fed7e58c86d5e981dbc3852a31c8f452a88dd858 Mon Sep 17 00:00:00 2001 From: "Sam A. Horvath-Hunt" Date: Mon, 3 Jun 2019 18:47:29 +0100 Subject: [PATCH] Update API for POST/PUT/DELETE to expect an array --- src/buku/database.rs | 89 +++++++++++++++++++++------------------- src/server.rs | 98 +++++++++++++++++++++++++------------------- 2 files changed, 103 insertions(+), 84 deletions(-) diff --git a/src/buku/database.rs b/src/buku/database.rs index 6c3e8a9..33cd9e2 100644 --- a/src/buku/database.rs +++ b/src/buku/database.rs @@ -6,9 +6,9 @@ use std::path::PathBuf; pub trait BukuDatabase { fn get_all_bookmarks(&self) -> Result, DbError>; fn get_bookmarks_by_id(&self, ids: Vec) -> Result, DbError>; - fn add_bookmark(&self, bm: &UnsavedBookmark) -> Result; - fn update_bookmark(&self, bm: &SavedBookmark) -> Result; - fn delete_bookmark(&self, bm_id: &BookmarkId) -> Result; + fn add_bookmarks(&self, bms: &Vec) -> Result, DbError>; + fn update_bookmarks(&self, bms: &Vec) -> Result, DbError>; + fn delete_bookmarks(&self, bm_id: &Vec) -> Result, DbError>; } pub struct SqliteDatabase { @@ -39,7 +39,6 @@ fn map_db_bookmark(row: &Row) -> Result { } impl BukuDatabase for SqliteDatabase { - // Get bookmarks from database fn get_all_bookmarks(&self) -> Result, DbError> { let query = "SELECT * FROM bookmarks;"; let mut stmt = self.connection.prepare(query)?; @@ -70,47 +69,53 @@ impl BukuDatabase for SqliteDatabase { Ok(bookmarks) } - // Save bookmark to database - fn add_bookmark(&self, bm: &UnsavedBookmark) -> Result { - let query = - "INSERT INTO bookmarks(metadata, desc, tags, url, flags) VALUES (?1, ?2, ?3, ?4, ?5);"; - let exec = self.connection.execute( - query, - &[ - &bm.metadata, - &bm.desc, - &bm.tags, - &bm.url, - &bm.flags as &ToSql, - ], - ); - - exec + fn add_bookmarks(&self, bms: &Vec) -> Result, DbError> { + bms + .iter() + .map(|bm| { + let query = + "INSERT INTO bookmarks(metadata, desc, tags, url, flags) VALUES (?1, ?2, ?3, ?4, ?5);"; + self.connection.execute( + query, + &[ + &bm.metadata, + &bm.desc, + &bm.tags, + &bm.url, + &bm.flags as &ToSql, + ], + ) + }) + .collect() } - // Update bookmark in database by ID - fn update_bookmark(&self, bm: &SavedBookmark) -> Result { - let query = "UPDATE bookmarks SET (metadata, desc, tags, url, flags) = (?2, ?3, ?4, ?5, ?6) WHERE id = ?1;"; - let exec = self.connection.execute( - query, - &[ - &bm.id, - &bm.metadata as &ToSql, - &bm.desc, - &bm.tags, - &bm.url, - &bm.flags, - ], - ); - - exec + fn update_bookmarks(&self, bms: &Vec) -> Result, DbError> { + bms + .iter() + .map(|bm| { + let query = "UPDATE bookmarks SET (metadata, desc, tags, url, flags) = (?2, ?3, ?4, ?5, ?6) WHERE id = ?1;"; + self.connection.execute( + query, + &[ + &bm.id, + &bm.metadata as &ToSql, + &bm.desc, + &bm.tags, + &bm.url, + &bm.flags, + ], + ) + }) + .collect() } - // Delete bookmark from database by ID - fn delete_bookmark(&self, bm_id: &BookmarkId) -> Result { - let query = "DELETE FROM bookmarks WHERE id = ?1;"; - let exec = self.connection.execute(query, &[bm_id]); - - exec + fn delete_bookmarks(&self, bm_ids: &Vec) -> Result, DbError> { + bm_ids + .iter() + .map(|bm_id| { + let query = "DELETE FROM bookmarks WHERE id = ?1;"; + self.connection.execute(query, &[bm_id]) + }) + .collect() } } diff --git a/src/server.rs b/src/server.rs index 0d83703..84c0387 100644 --- a/src/server.rs +++ b/src/server.rs @@ -43,21 +43,21 @@ struct RequestData { #[derive(Deserialize)] struct RequestDataPost { - bookmark: UnsavedBookmark, + bookmarks: Vec, } type PostRequest = RequestData; #[derive(Deserialize)] struct RequestDataPut { - bookmark: SavedBookmark, + bookmarks: Vec, } type PutRequest = RequestData; #[derive(Deserialize)] struct RequestDataDelete { - bookmark_id: BookmarkId, + bookmark_ids: Vec, } type DeleteRequest = RequestData; @@ -109,13 +109,13 @@ impl Server { Method::Get => self.get(&db), Method::Options => self.options(), Method::Post => serde_json::from_value::(payload) - .map(|req| self.post(&db, &req.data.bookmark)) + .map(|req| self.post(&db, &req.data.bookmarks)) .unwrap_or_else(|_| self.fail_bad_payload()), Method::Put => serde_json::from_value::(payload) - .map(|req| self.put(&db, &req.data.bookmark)) + .map(|req| self.put(&db, &req.data.bookmarks)) .unwrap_or_else(|_| self.fail_bad_payload()), Method::Delete => serde_json::from_value::(payload) - .map(|req| self.delete(&db, &req.data.bookmark_id)) + .map(|req| self.delete(&db, &req.data.bookmark_ids)) .unwrap_or_else(|_| self.fail_bad_payload()), Method::UnknownMethod => self.fail_unknown_method(), Method::NoMethod => self.fail_no_method(), @@ -143,27 +143,27 @@ impl Server { }) } - fn post(&self, db: &T, bm: &UnsavedBookmark) -> JSON { - let added = db.add_bookmark(&bm); + fn post(&self, db: &T, bms: &Vec) -> JSON { + let added = db.add_bookmarks(&bms); - if let Ok(id) = added { + if let Ok(ids) = added { json!({ "success": true, - "id": id, + "ids": ids, }) } else { self.fail_generic() } } - fn put(&self, db: &T, bm: &SavedBookmark) -> JSON { - let update = db.update_bookmark(&bm); + fn put(&self, db: &T, bms: &Vec) -> JSON { + let update = db.update_bookmarks(&bms); json!({ "success": update.is_ok() }) } - fn delete(&self, db: &T, bm_id: &BookmarkId) -> JSON { - let deletion = db.delete_bookmark(&bm_id); + fn delete(&self, db: &T, bm_ids: &Vec) -> JSON { + let deletion = db.delete_bookmarks(&bm_ids); json!({ "success": deletion.is_ok() }) } @@ -206,8 +206,8 @@ mod tests { use super::*; use crate::buku::database::{BukuDatabase, DbError, SqliteDatabase}; - fn shared_mock_update_id() -> usize { - 1234 + fn shared_mock_update_ids() -> Vec { + vec![1, 2, 3, 4] } fn create_mocked_server() -> Server { @@ -225,16 +225,16 @@ mod tests { Ok(Vec::new()) } - fn add_bookmark(&self, _bm: &UnsavedBookmark) -> Result { - Ok(shared_mock_update_id()) + fn add_bookmarks(&self, _bm: &Vec) -> Result, DbError> { + Ok(shared_mock_update_ids()) } - fn update_bookmark(&self, _bm: &SavedBookmark) -> Result { - Ok(shared_mock_update_id()) + fn update_bookmarks(&self, _bm: &Vec) -> Result, DbError> { + Ok(shared_mock_update_ids()) } - fn delete_bookmark(&self, _bm_id: &BookmarkId) -> Result { - Ok(shared_mock_update_id()) + fn delete_bookmarks(&self, _bm_ids: &Vec) -> Result, DbError> { + Ok(shared_mock_update_ids()) } } @@ -247,25 +247,29 @@ mod tests { Server { db: Err(err) } } - fn create_example_saved_bookmark() -> SavedBookmark { - SavedBookmark { - id: 0, - url: String::from("https://samhh.com"), - metadata: String::from("title"), - tags: String::from(""), - desc: String::from("description"), - flags: 0, - } + fn create_example_saved_bookmarks() -> Vec { + vec![ + SavedBookmark { + id: 0, + url: String::from("https://samhh.com"), + metadata: String::from("title"), + tags: String::from(""), + desc: String::from("description"), + flags: 0, + } + ] } - fn create_example_unsaved_bookmark() -> UnsavedBookmark { - UnsavedBookmark { - url: String::from("https://samhh.com"), - metadata: String::from("title"), - tags: String::from(""), - desc: String::from("description"), - flags: 0, - } + fn create_example_unsaved_bookmarks() -> Vec { + vec![ + UnsavedBookmark { + url: String::from("https://samhh.com"), + metadata: String::from("title"), + tags: String::from(""), + desc: String::from("description"), + flags: 0, + } + ] } #[test] @@ -345,12 +349,12 @@ mod tests { server.router(json!({ "method": "POST", "data": { - "bookmark": create_example_unsaved_bookmark(), + "bookmarks": create_example_unsaved_bookmarks(), }, })), json!({ "success": true, - "id": shared_mock_update_id(), + "ids": shared_mock_update_ids(), }), ); } @@ -368,7 +372,7 @@ mod tests { server.router(json!({ "method": "PUT", "data": { - "bookmark": create_example_saved_bookmark(), + "bookmarks": create_example_saved_bookmarks(), }, })), json!({ "success": true }), @@ -407,6 +411,16 @@ mod tests { "bookmark_id": 99, }, })), + server.fail_bad_payload(), + ); + + assert_eq!( + server.router(json!({ + "method": "DELETE", + "data": { + "bookmark_ids": vec![99], + }, + })), json!({ "success": true }), ); }