diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b993f6..54cf108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Make `Client::with_base_url()` a method instead of a constructor [[#3](https://github.com/kuy/jsonbox-rs/issues/3)] - Change visibility of `Client::read_by_id()` and `Client::read_by_query()` (accidently exposed in past versions) [[#2](https://github.com/kuy/jsonbox-rs/issues/2)] +### Added + +- Support bulk `CREATE` operation [[#9](https://github.com/kuy/jsonbox-rs/issues/9)] + ## [0.1.2] 2019-09-28 ### Added diff --git a/examples/basic.rs b/examples/basic.rs index 8036eff..6d5181a 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -23,16 +23,16 @@ impl Data { fn main() -> Result<(), Error> { let client = Client::new("box_8ed82aef3f93176996145"); - let data = Data::new("jsonbox", 42, false); + let mut data = Data::new("kuy", 42, false); let (record, meta1) = client.create(&data)?; println!("CREATE: data={:?}, meta={:?}", record, meta1); let (record, meta1) = client.read().id::(&meta1.id)?; println!("READ single: data={:?}, meta={:?}", record, meta1); - let mut data = Data::new("kuy", 16, false); - let (record, meta2) = client.create(&data)?; - println!("CREATE: data={:?}, meta={:?}", record, meta2); + let list = vec![Data::new("jsonbox", 21, false), Data::new("io", 16, true)]; + let bulk = client.create_bulk(&list)?; + println!("CREATE bulk: len={}, bulk={:?}", bulk.len(), bulk); let all = client.read().all::()?; println!("READ: len={}, all={:?}", all.len(), all); @@ -43,8 +43,8 @@ fn main() -> Result<(), Error> { let few = client.read().limit(1).run::()?; println!("READ: len={}, few={:?}", few.len(), few); - data.name = format!("kuy {}", meta2.id); - client.update(&meta2.id, &data)?; + data.name = format!("kuy {}", meta1.id); + client.update(&meta1.id, &data)?; println!("UPDATE: OK"); let filtered = client @@ -58,12 +58,12 @@ fn main() -> Result<(), Error> { let filtered = client .read() - .filter_by("name:*{}", &meta2.id) + .filter_by("name:*{}", &meta1.id) .run::()?; println!("READ: len={}, filtered={:?}", filtered.len(), filtered); data.age = 8; - client.update(&meta2.id, &data)?; + client.update(&meta1.id, &data)?; println!("UPDATE: OK"); let filtered = client @@ -76,8 +76,5 @@ fn main() -> Result<(), Error> { client.delete(&meta1.id)?; println!("DELETE: OK"); - client.delete(&meta2.id)?; - println!("DELETE: OK"); - Ok(()) } diff --git a/src/client/mod.rs b/src/client/mod.rs index 3193e53..c8e54ae 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -2,6 +2,7 @@ pub mod query_builder; use reqwest; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::from_str; use snafu::ResultExt; use std::convert::From; @@ -75,9 +76,8 @@ impl<'a> Client<'a> { .context(error::Network {})?; if res.status().is_success() { let raw = res.text().context(error::Network {})?; - let data: T = serde_json::from_str(&raw).context(error::Json { reason: "data" })?; - let meta: MetaRaw = - serde_json::from_str(&raw).context(error::Json { reason: "meta" })?; + let data: T = from_str(&raw).context(error::Json { reason: "data" })?; + let meta: MetaRaw = from_str(&raw).context(error::Json { reason: "meta" })?; Ok((data, Meta::from(meta))) } else { let err: ErrorMessage = res.json().context(error::Network {})?; @@ -88,6 +88,33 @@ impl<'a> Client<'a> { } } + pub fn create_bulk(&self, data: &Vec) -> Result> + where + T: Serialize + DeserializeOwned, + { + let client = reqwest::Client::new(); + let mut res = client + .post(&url::of_box(&self.base_url, &self.box_id)) + .json(&data) + .send() + .context(error::Network {})?; + if res.status().is_success() { + let raw = res.text().context(error::Network {})?; + let data: Vec = from_str(&raw).context(error::Json { reason: "data" })?; + let meta: Vec = from_str(&raw).context(error::Json { reason: "meta" })?; + Ok(data + .into_iter() + .zip(meta.into_iter().map(|meta| Meta::from(meta))) + .collect()) + } else { + let err: ErrorMessage = res.json().context(error::Network {})?; + Err(Error::General { + code: res.status().as_u16(), + message: err.message, + }) + } + } + pub fn read(&self) -> QueryBuilder { QueryBuilder::new(self) } @@ -100,9 +127,8 @@ impl<'a> Client<'a> { let mut res = reqwest::get(&url).context(error::Network {})?; if res.status().is_success() { let raw = res.text().context(error::Network {})?; - let data: T = serde_json::from_str(&raw).context(error::Json { reason: "data" })?; - let meta: MetaRaw = - serde_json::from_str(&raw).context(error::Json { reason: "meta" })?; + let data: T = from_str(&raw).context(error::Json { reason: "data" })?; + let meta: MetaRaw = from_str(&raw).context(error::Json { reason: "meta" })?; Ok((data, Meta::from(meta))) } else { let err: ErrorMessage = res.json().context(error::Network {})?; @@ -121,10 +147,8 @@ impl<'a> Client<'a> { let mut res = reqwest::get(url).context(error::Network {})?; if res.status().is_success() { let raw = res.text().context(error::Network {})?; - let data: Vec = - serde_json::from_str(&raw).context(error::Json { reason: "data" })?; - let meta: Vec = - serde_json::from_str(&raw).context(error::Json { reason: "meta" })?; + let data: Vec = from_str(&raw).context(error::Json { reason: "data" })?; + let meta: Vec = from_str(&raw).context(error::Json { reason: "meta" })?; Ok(data .into_iter() .zip(meta.into_iter().map(|meta| Meta::from(meta))) diff --git a/tests/client.rs b/tests/client.rs index dda5f7a..afd6c4b 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -33,6 +33,40 @@ fn test_create() { assert_eq!(meta.updated_on, "2019-09-22T12:24:37.513Z"); } +#[test] +fn test_create_bulk() { + let _m = mock("POST", "/00000000000000000000") + .with_status(200) + .with_header("content-type", "application/json; charset=utf-8") + .with_body(r#"[{"_id":"11111111111111111111","name":"rust","count":42,"_createdOn":"2019-09-22T12:24:37.513Z"},{"_id":"22222222222222222222","name":"cargo","count":7,"_createdOn":"2019-09-22T12:24:37.513Z"}]"#) + .create(); + let server_url = mockito::server_url(); + let client = Client::new("00000000000000000000").with_base_url(&server_url); + let data = vec![ + Data { + name: "rust".into(), + count: 42, + }, + Data { + name: "cargo".into(), + count: 7, + }, + ]; + let res = client.create_bulk(&data); + assert!(res.is_ok()); + + let bulk = res.unwrap(); + assert_eq!(bulk.len(), 2); + + let (data, meta) = bulk.first().unwrap(); + assert_eq!(data.name, "rust"); + assert_eq!(meta.id, "11111111111111111111"); + + let (data, meta) = bulk.last().unwrap(); + assert_eq!(data.name, "cargo"); + assert_eq!(meta.id, "22222222222222222222"); +} + #[test] fn test_read_all() { let _m = mock("GET", "/00000000000000000000")