Better error handling in create_transaction

This commit is contained in:
Tyler Hallada 2020-11-02 01:38:29 -05:00
parent a5e3b402fa
commit dcc3590ac3
5 changed files with 53 additions and 18 deletions

12
Cargo.lock generated
View File

@ -110,6 +110,7 @@ dependencies = [
"bytes", "bytes",
"cbindgen", "cbindgen",
"dirs", "dirs",
"http-api-problem",
"log", "log",
"mockito", "mockito",
"reqwest", "reqwest",
@ -476,6 +477,17 @@ dependencies = [
"itoa", "itoa",
] ]
[[package]]
name = "http-api-problem"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4760c8ea7ae413a5e4d122c83beb37ae0baf9b978af55dc8a161b7fd5fcd6458"
dependencies = [
"http",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "0.3.1" version = "0.3.1"

View File

@ -13,6 +13,7 @@ cbindgen = "0.14.4"
anyhow = "1.0" anyhow = "1.0"
base64 = "0.13" base64 = "0.13"
bytes = "0.5" bytes = "0.5"
http-api-problem = "0.17"
mockito = "0.26.0" mockito = "0.26.0"
reqwest = { version = "0.10", features = ["blocking", "json", "gzip"] } reqwest = { version = "0.10", features = ["blocking", "json", "gzip"] }
log = "0.4" log = "0.4"

View File

@ -5,9 +5,6 @@
#include <cassert> #include <cassert>
template<typename T = void>
struct Option;
template<typename T> template<typename T>
struct FFIResult { struct FFIResult {
enum class Tag : uint8_t { enum class Tag : uint8_t {
@ -99,7 +96,7 @@ struct RawShop {
}; };
struct RawTransaction { struct RawTransaction {
Option<uint32_t> id; uint32_t id;
uint32_t shop_id; uint32_t shop_id;
const char *mod_name; const char *mod_name;
uint32_t local_form_id; uint32_t local_form_id;
@ -139,6 +136,7 @@ struct _Helper_0 {
FFIResult<RawShopVec> _raw_shop_vec_result; FFIResult<RawShopVec> _raw_shop_vec_result;
FFIResult<RawInteriorRefVec> _raw_interior_ref_vec_result; FFIResult<RawInteriorRefVec> _raw_interior_ref_vec_result;
FFIResult<RawMerchandiseVec> _raw_merchandise_vec_result; FFIResult<RawMerchandiseVec> _raw_merchandise_vec_result;
FFIResult<RawTransaction> _raw_transaction_result;
}; };
// dummy extern C block to close curly brace (did I mention this is a bad hack?) // dummy extern C block to close curly brace (did I mention this is a bad hack?)

View File

@ -77,6 +77,7 @@ struct _Helper_0 {
FFIResult<RawShopVec> _raw_shop_vec_result; FFIResult<RawShopVec> _raw_shop_vec_result;
FFIResult<RawInteriorRefVec> _raw_interior_ref_vec_result; FFIResult<RawInteriorRefVec> _raw_interior_ref_vec_result;
FFIResult<RawMerchandiseVec> _raw_merchandise_vec_result; FFIResult<RawMerchandiseVec> _raw_merchandise_vec_result;
FFIResult<RawTransaction> _raw_transaction_result;
}; };
// dummy extern C block to close curly brace (did I mention this is a bad hack?) // dummy extern C block to close curly brace (did I mention this is a bad hack?)

View File

@ -1,6 +1,7 @@
use std::{convert::TryFrom, ffi::CStr, ffi::CString, os::raw::c_char, slice}; use std::{convert::TryFrom, ffi::CStr, ffi::CString, os::raw::c_char, slice, str};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use http_api_problem::HttpApiProblem;
use reqwest::Url; use reqwest::Url;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -61,7 +62,10 @@ impl Transaction {
impl From<RawTransaction> for Transaction { impl From<RawTransaction> for Transaction {
fn from(raw_transaction: RawTransaction) -> Self { fn from(raw_transaction: RawTransaction) -> Self {
Self { Self {
id: raw_transaction.id, id: match raw_transaction.id {
0 => None,
_ => Some(raw_transaction.id),
},
shop_id: raw_transaction.shop_id, shop_id: raw_transaction.shop_id,
mod_name: unsafe { CStr::from_ptr(raw_transaction.mod_name) } mod_name: unsafe { CStr::from_ptr(raw_transaction.mod_name) }
.to_string_lossy() .to_string_lossy()
@ -83,7 +87,7 @@ impl From<RawTransaction> for Transaction {
#[derive(Debug)] #[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct RawTransaction { pub struct RawTransaction {
pub id: Option<u32>, pub id: u32,
pub shop_id: u32, pub shop_id: u32,
pub mod_name: *const c_char, pub mod_name: *const c_char,
pub local_form_id: u32, pub local_form_id: u32,
@ -99,7 +103,7 @@ pub struct RawTransaction {
impl From<Transaction> for RawTransaction { impl From<Transaction> for RawTransaction {
fn from(transaction: Transaction) -> Self { fn from(transaction: Transaction) -> Self {
Self { Self {
id: transaction.id, id: transaction.id.unwrap_or(0),
shop_id: transaction.shop_id, shop_id: transaction.shop_id,
mod_name: CString::new(transaction.mod_name) mod_name: CString::new(transaction.mod_name)
.unwrap_or_default() .unwrap_or_default()
@ -153,7 +157,9 @@ pub extern "C" fn create_transaction(
.json(&transaction) .json(&transaction)
.send()?; .send()?;
info!("create transaction response from api: {:?}", &resp); info!("create transaction response from api: {:?}", &resp);
let status = resp.status();
let bytes = resp.bytes()?; let bytes = resp.bytes()?;
if status.is_success() {
let json: Transaction = serde_json::from_slice(&bytes)?; let json: Transaction = serde_json::from_slice(&bytes)?;
if let Some(id) = json.id { if let Some(id) = json.id {
update_file_cache( update_file_cache(
@ -162,6 +168,23 @@ pub extern "C" fn create_transaction(
)?; )?;
} }
Ok(json) Ok(json)
} else {
match serde_json::from_slice::<HttpApiProblem>(&bytes) {
Ok(api_problem) => {
let detail = api_problem.detail.unwrap_or("".to_string());
error!("Server {} error: {}. {}", status, api_problem.title, detail);
Err(anyhow!(format!(
"Server {} error: {}. {}",
status, api_problem.title, detail
)))
}
Err(_) => {
let detail = str::from_utf8(&bytes).unwrap_or("unknown");
error!("Server {} error: {}", status, detail);
Err(anyhow!(format!("Server {} error: {}", status, detail)))
}
}
}
} }
match inner(&api_url, &api_key, transaction) { match inner(&api_url, &api_key, transaction) {
@ -226,7 +249,7 @@ mod tests {
let mod_name = CString::new("Skyrim.esm").unwrap().into_raw(); let mod_name = CString::new("Skyrim.esm").unwrap().into_raw();
let name = CString::new("Item").unwrap().into_raw(); let name = CString::new("Item").unwrap().into_raw();
let raw_transaction = RawTransaction { let raw_transaction = RawTransaction {
id: None, id: 0,
shop_id: 1, shop_id: 1,
mod_name, mod_name,
local_form_id: 1, local_form_id: 1,
@ -242,7 +265,7 @@ mod tests {
mock.assert(); mock.assert();
match result { match result {
FFIResult::Ok(raw_transaction) => { FFIResult::Ok(raw_transaction) => {
assert_eq!(raw_transaction.id, Some(1)); assert_eq!(raw_transaction.id, 1);
assert_eq!(raw_transaction.shop_id, 1); assert_eq!(raw_transaction.shop_id, 1);
assert_eq!( assert_eq!(
unsafe { CStr::from_ptr(raw_transaction.mod_name).to_string_lossy() }, unsafe { CStr::from_ptr(raw_transaction.mod_name).to_string_lossy() },
@ -278,7 +301,7 @@ mod tests {
let mod_name = CString::new("Skyrim.esm").unwrap().into_raw(); let mod_name = CString::new("Skyrim.esm").unwrap().into_raw();
let name = CString::new("Item").unwrap().into_raw(); let name = CString::new("Item").unwrap().into_raw();
let raw_transaction = RawTransaction { let raw_transaction = RawTransaction {
id: None, id: 0,
shop_id: 1, shop_id: 1,
mod_name, mod_name,
local_form_id: 1, local_form_id: 1,