Write metadata to file cache
Then, before a GET, read the metadata and add any etag to the headers and handle 304 in response.
This commit is contained in:
parent
dcc3590ac3
commit
127a68687e
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -109,6 +109,7 @@ dependencies = [
|
|||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
|
"chrono",
|
||||||
"dirs",
|
"dirs",
|
||||||
"http-api-problem",
|
"http-api-problem",
|
||||||
"log",
|
"log",
|
||||||
@ -183,6 +184,20 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"time",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.1"
|
version = "2.33.1"
|
||||||
@ -742,6 +757,25 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
|
@ -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"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
http-api-problem = "0.17"
|
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"] }
|
||||||
|
53
src/cache.rs
53
src/cache.rs
@ -3,7 +3,9 @@ use std::{fs::create_dir_all, fs::File, io::BufReader, io::Write, path::Path, pa
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use base64::{encode_config, URL_SAFE_NO_PAD};
|
use base64::{encode_config, URL_SAFE_NO_PAD};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use serde::Deserialize;
|
use chrono::{DateTime, Utc};
|
||||||
|
use reqwest::{blocking::Response, header::HeaderMap};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use tempfile::tempfile;
|
use tempfile::tempfile;
|
||||||
|
|
||||||
@ -14,6 +16,12 @@ use std::println as info;
|
|||||||
|
|
||||||
use super::API_VERSION;
|
use super::API_VERSION;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Metadata {
|
||||||
|
pub etag: Option<String>,
|
||||||
|
pub date: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn file_cache_dir(api_url: &str) -> Result<PathBuf> {
|
pub fn file_cache_dir(api_url: &str) -> Result<PathBuf> {
|
||||||
let encoded_url = encode_config(api_url, URL_SAFE_NO_PAD);
|
let encoded_url = encode_config(api_url, URL_SAFE_NO_PAD);
|
||||||
let path = Path::new("Data/SKSE/Plugins/BazaarRealmCache")
|
let path = Path::new("Data/SKSE/Plugins/BazaarRealmCache")
|
||||||
@ -34,6 +42,34 @@ pub fn update_file_cache(cache_path: &Path, bytes: &Bytes) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_metadata_file_cache(cache_path: &Path, headers: &HeaderMap) -> Result<()> {
|
||||||
|
#[cfg(not(test))]
|
||||||
|
let mut file = File::create(cache_path)?;
|
||||||
|
#[cfg(test)]
|
||||||
|
let mut file = tempfile()?;
|
||||||
|
|
||||||
|
let etag = headers
|
||||||
|
.get("etag")
|
||||||
|
.map(|val| val.to_str().unwrap_or("").to_string());
|
||||||
|
let date = headers
|
||||||
|
.get("date")
|
||||||
|
.map(|val| val.to_str().unwrap_or("").parse().unwrap_or(Utc::now()));
|
||||||
|
let metadata = Metadata { etag, date };
|
||||||
|
serde_json::to_writer(file, &metadata)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_file_caches(
|
||||||
|
body_cache_path: &Path,
|
||||||
|
metadata_cache_path: &Path,
|
||||||
|
response: Response,
|
||||||
|
) -> Result<Bytes> {
|
||||||
|
update_metadata_file_cache(metadata_cache_path, &response.headers())?;
|
||||||
|
let bytes = response.bytes()?;
|
||||||
|
update_file_cache(body_cache_path, &bytes)?;
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_file_cache<T: for<'de> Deserialize<'de>>(cache_path: &Path) -> Result<T> {
|
pub fn from_file_cache<T: for<'de> Deserialize<'de>>(cache_path: &Path) -> Result<T> {
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
let file = File::open(cache_path).context(format!(
|
let file = File::open(cache_path).context(format!(
|
||||||
@ -47,3 +83,18 @@ pub fn from_file_cache<T: for<'de> Deserialize<'de>>(cache_path: &Path) -> Resul
|
|||||||
info!("returning value from cache: {:?}", cache_path);
|
info!("returning value from cache: {:?}", cache_path);
|
||||||
Ok(serde_json::from_reader(reader)?)
|
Ok(serde_json::from_reader(reader)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_metadata_from_file_cache(cache_path: &Path) -> Result<Metadata> {
|
||||||
|
#[cfg(not(test))]
|
||||||
|
let file = File::open(cache_path).context(format!(
|
||||||
|
"Object not found in API or in cache: {}",
|
||||||
|
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
||||||
|
))?;
|
||||||
|
#[cfg(test)]
|
||||||
|
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
||||||
|
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
info!("returning value from cache: {:?}", cache_path);
|
||||||
|
let metadata: Metadata = serde_json::from_reader(reader)?;
|
||||||
|
Ok(metadata)
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{ffi::CStr, ffi::CString, os::raw::c_char, slice};
|
use std::{ffi::CStr, ffi::CString, os::raw::c_char, slice};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use reqwest::Url;
|
use reqwest::{StatusCode, Url};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
@ -10,8 +10,9 @@ use log::{error, info};
|
|||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::update_file_cache, log_server_error,
|
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
||||||
result::FFIResult,
|
cache::update_file_cache, cache::update_file_caches, cache::update_metadata_file_cache,
|
||||||
|
log_server_error, result::FFIResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -94,6 +95,7 @@ pub struct RawInteriorRefVec {
|
|||||||
pub cap: usize,
|
pub cap: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: delete me if unused
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn create_interior_ref_list(
|
pub extern "C" fn create_interior_ref_list(
|
||||||
api_url: *const c_char,
|
api_url: *const c_char,
|
||||||
@ -133,13 +135,20 @@ pub extern "C" fn create_interior_ref_list(
|
|||||||
.json(&interior_ref_list)
|
.json(&interior_ref_list)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("create interior_ref_list response from api: {:?}", &resp);
|
info!("create interior_ref_list response from api: {:?}", &resp);
|
||||||
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let headers = resp.headers().clone();
|
||||||
let bytes = resp.bytes()?;
|
let bytes = resp.bytes()?;
|
||||||
let json: InteriorRefList = serde_json::from_slice(&bytes)?;
|
let json: InteriorRefList = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
if let Some(id) = json.id {
|
||||||
update_file_cache(
|
update_file_cache(
|
||||||
&file_cache_dir(api_url)?.join(format!("interior_ref_list_{}.json", id)),
|
&cache_dir.join(format!("interior_ref_list_{}.json", id)),
|
||||||
&bytes,
|
&bytes,
|
||||||
)?;
|
)?;
|
||||||
|
update_metadata_file_cache(
|
||||||
|
&cache_dir.join(format!("interior_ref_list_{}_metadata.json", id)),
|
||||||
|
&headers,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
@ -209,14 +218,13 @@ pub extern "C" fn update_interior_ref_list(
|
|||||||
.json(&interior_ref_list)
|
.json(&interior_ref_list)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("update interior_ref_list response from api: {:?}", &resp);
|
info!("update interior_ref_list response from api: {:?}", &resp);
|
||||||
let bytes = resp.bytes()?;
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let body_cache_path = cache_dir.join(format!("shops_{}_interior_ref_list.json", shop_id));
|
||||||
|
let metadata_cache_path =
|
||||||
|
cache_dir.join(format!("shops_{}_interior_ref_list_metadata.json", shop_id));
|
||||||
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
let json: InteriorRefList = serde_json::from_slice(&bytes)?;
|
let json: InteriorRefList = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(_id) = json.id {
|
|
||||||
update_file_cache(
|
|
||||||
&file_cache_dir(api_url)?.join(format!("shops_{}_interior_ref_list.json", shop_id)),
|
|
||||||
&bytes,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +253,7 @@ pub extern "C" fn update_interior_ref_list(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: delete me if unused
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn get_interior_ref_list(
|
pub extern "C" fn get_interior_ref_list(
|
||||||
api_url: *const c_char,
|
api_url: *const c_char,
|
||||||
@ -268,25 +277,38 @@ pub extern "C" fn get_interior_ref_list(
|
|||||||
info!("api_url: {:?}", url);
|
info!("api_url: {:?}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let cache_path = file_cache_dir(api_url)?
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
.join(format!("interior_ref_list_{}.json", interior_ref_list_id));
|
let body_cache_path =
|
||||||
|
cache_dir.join(format!("interior_ref_list_{}.json", interior_ref_list_id));
|
||||||
|
let metadata_cache_path = cache_dir.join(format!(
|
||||||
|
"interior_ref_list_{}_metadata.json",
|
||||||
|
interior_ref_list_id
|
||||||
|
));
|
||||||
|
let mut request = client.get(url).header("Api-Key", api_key);
|
||||||
|
// TODO: load metadata from in-memory LRU cache first before trying to load from file
|
||||||
|
if let Ok(metadata) = load_metadata_from_file_cache(&metadata_cache_path) {
|
||||||
|
if let Some(etag) = metadata.etag {
|
||||||
|
request = request.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match client.get(url).header("Api-Key", api_key).send() {
|
match request.send() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
info!("get_interior_ref_list response from api: {:?}", &resp);
|
info!("get_interior_ref_list response from api: {:?}", &resp);
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
let bytes = resp.bytes()?;
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
update_file_cache(&cache_path, &bytes)?;
|
|
||||||
let json = serde_json::from_slice(&bytes)?;
|
let json = serde_json::from_slice(&bytes)?;
|
||||||
Ok(json)
|
Ok(json)
|
||||||
|
} else if resp.status() == StatusCode::NOT_MODIFIED {
|
||||||
|
from_file_cache(&body_cache_path)
|
||||||
} else {
|
} else {
|
||||||
log_server_error(resp);
|
log_server_error(resp);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_interior_ref_list api request error: {}", err);
|
error!("get_interior_ref_list api request error: {}", err);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,23 +377,33 @@ pub extern "C" fn get_interior_ref_list_by_shop_id(
|
|||||||
info!("api_url: {:?}", url);
|
info!("api_url: {:?}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let cache_path =
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
file_cache_dir(api_url)?.join(format!("shops_{}_interior_ref_list.json", shop_id));
|
let body_cache_path = cache_dir.join(format!("shops_{}_interior_ref_list.json", shop_id));
|
||||||
|
let metadata_cache_path =
|
||||||
|
cache_dir.join(format!("shops_{}_interior_ref_list_metadata.json", shop_id));
|
||||||
|
let mut request = client.get(url).header("Api-Key", api_key);
|
||||||
|
// TODO: load metadata from in-memory LRU cache first before trying to load from file
|
||||||
|
if let Ok(metadata) = load_metadata_from_file_cache(&metadata_cache_path) {
|
||||||
|
if let Some(etag) = metadata.etag {
|
||||||
|
request = request.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match client.get(url).header("Api-Key", api_key).send() {
|
match request.send() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
info!(
|
info!(
|
||||||
"get_interior_ref_list_by_shop_id response from api: {:?}",
|
"get_interior_ref_list_by_shop_id response from api: {:?}",
|
||||||
&resp
|
&resp
|
||||||
);
|
);
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
let bytes = resp.bytes()?;
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
update_file_cache(&cache_path, &bytes)?;
|
|
||||||
let json = serde_json::from_slice(&bytes)?;
|
let json = serde_json::from_slice(&bytes)?;
|
||||||
Ok(json)
|
Ok(json)
|
||||||
|
} else if resp.status() == StatusCode::NOT_MODIFIED {
|
||||||
|
from_file_cache(&body_cache_path)
|
||||||
} else {
|
} else {
|
||||||
log_server_error(resp);
|
log_server_error(resp);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -379,7 +411,7 @@ pub extern "C" fn get_interior_ref_list_by_shop_id(
|
|||||||
"get_interior_ref_list_by_shop_id api request error: {}",
|
"get_interior_ref_list_by_shop_id api request error: {}",
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{ffi::CStr, ffi::CString, os::raw::c_char, slice};
|
use std::{ffi::CStr, ffi::CString, os::raw::c_char, slice};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use reqwest::Url;
|
use reqwest::{StatusCode, Url};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
@ -10,18 +10,19 @@ use log::{error, info};
|
|||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::update_file_cache, log_server_error,
|
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
||||||
result::FFIResult,
|
cache::update_file_cache, cache::update_file_caches, cache::update_metadata_file_cache,
|
||||||
|
log_server_error, result::FFIResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct MerchandiseList {
|
pub struct MerchandiseList {
|
||||||
pub id: Option<i32>,
|
pub id: Option<i32>,
|
||||||
pub shop_id: i32,
|
pub shop_id: i32,
|
||||||
pub form_list: Vec<Merchandise>,
|
pub form_list: Vec<Merchandise>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Merchandise {
|
pub struct Merchandise {
|
||||||
pub mod_name: String,
|
pub mod_name: String,
|
||||||
pub local_form_id: u32,
|
pub local_form_id: u32,
|
||||||
@ -77,6 +78,7 @@ pub struct RawMerchandiseVec {
|
|||||||
pub cap: usize,
|
pub cap: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: delete me if unused
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn create_merchandise_list(
|
pub extern "C" fn create_merchandise_list(
|
||||||
api_url: *const c_char,
|
api_url: *const c_char,
|
||||||
@ -116,13 +118,20 @@ pub extern "C" fn create_merchandise_list(
|
|||||||
.json(&merchandise_list)
|
.json(&merchandise_list)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("create merchandise_list response from api: {:?}", &resp);
|
info!("create merchandise_list response from api: {:?}", &resp);
|
||||||
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let headers = resp.headers().clone();
|
||||||
let bytes = resp.bytes()?;
|
let bytes = resp.bytes()?;
|
||||||
let json: MerchandiseList = serde_json::from_slice(&bytes)?;
|
let json: MerchandiseList = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
if let Some(id) = json.id {
|
||||||
update_file_cache(
|
update_file_cache(
|
||||||
&file_cache_dir(api_url)?.join(format!("merchandise_list_{}.json", id)),
|
&cache_dir.join(format!("merchandise_list_{}.json", id)),
|
||||||
&bytes,
|
&bytes,
|
||||||
)?;
|
)?;
|
||||||
|
update_metadata_file_cache(
|
||||||
|
&cache_dir.join(format!("merchandise_list_{}_metadata.json", id)),
|
||||||
|
&headers,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
@ -194,14 +203,13 @@ pub extern "C" fn update_merchandise_list(
|
|||||||
.json(&merchandise_list)
|
.json(&merchandise_list)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("update merchandise_list response from api: {:?}", &resp);
|
info!("update merchandise_list response from api: {:?}", &resp);
|
||||||
let bytes = resp.bytes()?;
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let body_cache_path = cache_dir.join(format!("shops_{}_merchandise_list.json", shop_id));
|
||||||
|
let metadata_cache_path =
|
||||||
|
cache_dir.join(format!("shops_{}_merchandise_list_metadata.json", shop_id));
|
||||||
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
let json: MerchandiseList = serde_json::from_slice(&bytes)?;
|
let json: MerchandiseList = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
|
||||||
update_file_cache(
|
|
||||||
&file_cache_dir(api_url)?.join(format!("shops_{}_merchandise_list.json", id)),
|
|
||||||
&bytes,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +240,7 @@ pub extern "C" fn update_merchandise_list(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: delete me if unused
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn get_merchandise_list(
|
pub extern "C" fn get_merchandise_list(
|
||||||
api_url: *const c_char,
|
api_url: *const c_char,
|
||||||
@ -256,25 +265,38 @@ pub extern "C" fn get_merchandise_list(
|
|||||||
info!("api_url: {:?}", url);
|
info!("api_url: {:?}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let cache_path =
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
file_cache_dir(api_url)?.join(format!("merchandise_list_{}.json", merchandise_list_id));
|
let body_cache_path =
|
||||||
|
cache_dir.join(format!("merchandise_list_{}.json", merchandise_list_id));
|
||||||
|
let metadata_cache_path = cache_dir.join(format!(
|
||||||
|
"merchandise_list_{}_metadata.json",
|
||||||
|
merchandise_list_id
|
||||||
|
));
|
||||||
|
let mut request = client.get(url).header("Api-Key", api_key);
|
||||||
|
// TODO: load metadata from in-memory LRU cache first before trying to load from file
|
||||||
|
if let Ok(metadata) = load_metadata_from_file_cache(&metadata_cache_path) {
|
||||||
|
if let Some(etag) = metadata.etag {
|
||||||
|
request = request.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match client.get(url).header("Api-Key", api_key).send() {
|
match request.send() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
info!("get_merchandise_list response from api: {:?}", &resp);
|
info!("get_merchandise_list response from api: {:?}", &resp);
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
let bytes = resp.bytes()?;
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
update_file_cache(&cache_path, &bytes)?;
|
|
||||||
let json = serde_json::from_slice(&bytes)?;
|
let json = serde_json::from_slice(&bytes)?;
|
||||||
Ok(json)
|
Ok(json)
|
||||||
|
} else if resp.status() == StatusCode::NOT_MODIFIED {
|
||||||
|
from_file_cache(&body_cache_path)
|
||||||
} else {
|
} else {
|
||||||
log_server_error(resp);
|
log_server_error(resp);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_merchandise_list api request error: {}", err);
|
error!("get_merchandise_list api request error: {}", err);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,28 +358,38 @@ pub extern "C" fn get_merchandise_list_by_shop_id(
|
|||||||
info!("api_url: {:?}", url);
|
info!("api_url: {:?}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let cache_path =
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
file_cache_dir(api_url)?.join(format!("shops_{}_merchandise_list.json", shop_id));
|
let body_cache_path = cache_dir.join(format!("shops_{}_merchandise_list.json", shop_id));
|
||||||
|
let metadata_cache_path =
|
||||||
|
cache_dir.join(format!("shops_{}_merchandise_list_metadata.json", shop_id));
|
||||||
|
let mut request = client.get(url).header("Api-Key", api_key);
|
||||||
|
// TODO: load metadata from in-memory LRU cache first before trying to load from file
|
||||||
|
if let Ok(metadata) = load_metadata_from_file_cache(&metadata_cache_path) {
|
||||||
|
if let Some(etag) = metadata.etag {
|
||||||
|
request = request.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match client.get(url).header("Api-Key", api_key).send() {
|
match request.send() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
info!(
|
info!(
|
||||||
"get_merchandise_list_by_shop_id response from api: {:?}",
|
"get_merchandise_list_by_shop_id response from api: {:?}",
|
||||||
&resp
|
&resp
|
||||||
);
|
);
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
let bytes = resp.bytes()?;
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
update_file_cache(&cache_path, &bytes)?;
|
|
||||||
let json = serde_json::from_slice(&bytes)?;
|
let json = serde_json::from_slice(&bytes)?;
|
||||||
Ok(json)
|
Ok(json)
|
||||||
|
} else if resp.status() == StatusCode::NOT_MODIFIED {
|
||||||
|
from_file_cache(&body_cache_path)
|
||||||
} else {
|
} else {
|
||||||
log_server_error(resp);
|
log_server_error(resp);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_merchandise_list_by_shop_id api request error: {}", err);
|
error!("get_merchandise_list_by_shop_id api request error: {}", err);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
src/owner.rs
27
src/owner.rs
@ -9,7 +9,10 @@ use log::{error, info};
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{cache::file_cache_dir, cache::update_file_cache, result::FFIResult};
|
use crate::{
|
||||||
|
cache::file_cache_dir, cache::update_file_cache, cache::update_file_caches,
|
||||||
|
cache::update_metadata_file_cache, result::FFIResult,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Owner {
|
pub struct Owner {
|
||||||
@ -69,12 +72,16 @@ pub extern "C" fn create_owner(
|
|||||||
.json(&owner)
|
.json(&owner)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("create owner response from api: {:?}", &resp);
|
info!("create owner response from api: {:?}", &resp);
|
||||||
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let headers = resp.headers().clone();
|
||||||
let bytes = resp.bytes()?;
|
let bytes = resp.bytes()?;
|
||||||
let json: Owner = serde_json::from_slice(&bytes)?;
|
let json: Owner = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
if let Some(id) = json.id {
|
||||||
update_file_cache(
|
update_file_cache(&cache_dir.join(format!("owner_{}.json", id)), &bytes)?;
|
||||||
&file_cache_dir(api_url)?.join(format!("owner_{}.json", id)),
|
update_metadata_file_cache(
|
||||||
&bytes,
|
&cache_dir.join(format!("owner_{}_metadata.json", id)),
|
||||||
|
&headers,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(json)
|
Ok(json)
|
||||||
@ -144,14 +151,12 @@ pub extern "C" fn update_owner(
|
|||||||
.json(&owner)
|
.json(&owner)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("update owner response from api: {:?}", &resp);
|
info!("update owner response from api: {:?}", &resp);
|
||||||
let bytes = resp.bytes()?;
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let body_cache_path = cache_dir.join(format!("owner_{}.json", id));
|
||||||
|
let metadata_cache_path = cache_dir.join(format!("owner_{}_metadata.json", id));
|
||||||
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
let json: Owner = serde_json::from_slice(&bytes)?;
|
let json: Owner = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
|
||||||
update_file_cache(
|
|
||||||
&file_cache_dir(api_url)?.join(format!("owner_{}.json", id)),
|
|
||||||
&bytes,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Ok(json)
|
Ok(json)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("api-key not defined"))
|
Err(anyhow!("api-key not defined"))
|
||||||
|
73
src/shop.rs
73
src/shop.rs
@ -1,7 +1,7 @@
|
|||||||
use std::{convert::TryFrom, ffi::CStr, ffi::CString, os::raw::c_char};
|
use std::{convert::TryFrom, ffi::CStr, ffi::CString, os::raw::c_char};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use reqwest::Url;
|
use reqwest::{StatusCode, Url};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
@ -10,8 +10,9 @@ use log::{error, info};
|
|||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::update_file_cache, log_server_error,
|
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
||||||
result::FFIResult,
|
cache::update_file_cache, cache::update_file_caches, cache::update_metadata_file_cache,
|
||||||
|
log_server_error, result::FFIResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -110,12 +111,16 @@ pub extern "C" fn create_shop(
|
|||||||
.json(&shop)
|
.json(&shop)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("create shop response from api: {:?}", &resp);
|
info!("create shop response from api: {:?}", &resp);
|
||||||
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let headers = resp.headers().clone();
|
||||||
let bytes = resp.bytes()?;
|
let bytes = resp.bytes()?;
|
||||||
let json: Shop = serde_json::from_slice(&bytes)?;
|
let json: Shop = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
if let Some(id) = json.id {
|
||||||
update_file_cache(
|
update_file_cache(&cache_dir.join(format!("shop_{}.json", id)), &bytes)?;
|
||||||
&file_cache_dir(api_url)?.join(format!("shop_{}.json", id)),
|
update_metadata_file_cache(
|
||||||
&bytes,
|
&cache_dir.join(format!("shop_{}_metadata.json", id)),
|
||||||
|
&headers,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(json)
|
Ok(json)
|
||||||
@ -179,14 +184,12 @@ pub extern "C" fn update_shop(
|
|||||||
.json(&shop)
|
.json(&shop)
|
||||||
.send()?;
|
.send()?;
|
||||||
info!("update shop response from api: {:?}", &resp);
|
info!("update shop response from api: {:?}", &resp);
|
||||||
let bytes = resp.bytes()?;
|
|
||||||
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let body_cache_path = cache_dir.join(format!("shop_{}.json", id));
|
||||||
|
let metadata_cache_path = cache_dir.join(format!("shop_{}_metadata.json", id));
|
||||||
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
let json: Shop = serde_json::from_slice(&bytes)?;
|
let json: Shop = serde_json::from_slice(&bytes)?;
|
||||||
if let Some(id) = json.id {
|
|
||||||
update_file_cache(
|
|
||||||
&file_cache_dir(api_url)?.join(format!("shop_{}.json", id)),
|
|
||||||
&bytes,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,24 +240,34 @@ pub extern "C" fn get_shop(
|
|||||||
info!("api_url: {:?}", url);
|
info!("api_url: {:?}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let cache_path = file_cache_dir(api_url)?.join(format!("shop_{}.json", shop_id));
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let body_cache_path = cache_dir.join(format!("shop_{}.json", shop_id));
|
||||||
|
let metadata_cache_path = cache_dir.join(format!("shop_{}_metadata.json", shop_id));
|
||||||
|
let mut request = client.get(url).header("Api-Key", api_key);
|
||||||
|
// TODO: load metadata from in-memory LRU cache first before trying to load from file
|
||||||
|
if let Ok(metadata) = load_metadata_from_file_cache(&metadata_cache_path) {
|
||||||
|
if let Some(etag) = metadata.etag {
|
||||||
|
request = request.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match client.get(url).header("Api-Key", api_key).send() {
|
match request.send() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
info!("get_shop response from api: {:?}", &resp);
|
info!("get_shop response from api: {:?}", &resp);
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
let bytes = resp.bytes()?;
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
update_file_cache(&cache_path, &bytes)?;
|
|
||||||
let json = serde_json::from_slice(&bytes)?;
|
let json = serde_json::from_slice(&bytes)?;
|
||||||
Ok(json)
|
Ok(json)
|
||||||
|
} else if resp.status() == StatusCode::NOT_MODIFIED {
|
||||||
|
from_file_cache(&body_cache_path)
|
||||||
} else {
|
} else {
|
||||||
log_server_error(resp);
|
log_server_error(resp);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_shop api request error: {}", err);
|
error!("get_shop api request error: {}", err);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,24 +314,34 @@ pub extern "C" fn list_shops(
|
|||||||
info!("api_url: {:?}", url);
|
info!("api_url: {:?}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let cache_path = file_cache_dir(api_url)?.join("shops.json");
|
let cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let body_cache_path = cache_dir.join("shops.json");
|
||||||
|
let metadata_cache_path = cache_dir.join("shops_metadata.json");
|
||||||
|
let mut request = client.get(url).header("Api-Key", api_key);
|
||||||
|
// TODO: load metadata from in-memory LRU cache first before trying to load from file
|
||||||
|
if let Ok(metadata) = load_metadata_from_file_cache(&metadata_cache_path) {
|
||||||
|
if let Some(etag) = metadata.etag {
|
||||||
|
request = request.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match client.get(url).header("Api-Key", api_key).send() {
|
match request.send() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
info!("list_shops response from api: {:?}", &resp);
|
info!("list_shops response from api: {:?}", &resp);
|
||||||
if resp.status().is_success() {
|
if resp.status().is_success() {
|
||||||
let bytes = resp.bytes()?;
|
let bytes = update_file_caches(&body_cache_path, &metadata_cache_path, resp)?;
|
||||||
update_file_cache(&cache_path, &bytes)?;
|
|
||||||
let json = serde_json::from_slice(&bytes)?;
|
let json = serde_json::from_slice(&bytes)?;
|
||||||
Ok(json)
|
Ok(json)
|
||||||
|
} else if resp.status() == StatusCode::NOT_MODIFIED {
|
||||||
|
from_file_cache(&body_cache_path)
|
||||||
} else {
|
} else {
|
||||||
log_server_error(resp);
|
log_server_error(resp);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("list_shops api request error: {}", err);
|
error!("list_shops api request error: {}", err);
|
||||||
from_file_cache(&cache_path)
|
from_file_cache(&body_cache_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ use log::{error, info};
|
|||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::update_file_cache, log_server_error,
|
cache::file_cache_dir, cache::from_file_cache, cache::update_file_cache,
|
||||||
result::FFIResult,
|
cache::update_metadata_file_cache, log_server_error, result::FFIResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -157,31 +157,43 @@ 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 cache_dir = file_cache_dir(api_url)?;
|
||||||
|
let headers = resp.headers().clone();
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
let bytes = resp.bytes()?;
|
let bytes = resp.bytes()?;
|
||||||
if status.is_success() {
|
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(&cache_dir.join(format!("transaction_{}.json", id)), &bytes)?;
|
||||||
&file_cache_dir(api_url)?.join(format!("transaction_{}.json", id)),
|
update_metadata_file_cache(
|
||||||
&bytes,
|
&cache_dir.join(format!("transaction_{}_metadata.json", id)),
|
||||||
|
&headers,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(json)
|
Ok(json)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: abstract this away into a separate helper
|
||||||
match serde_json::from_slice::<HttpApiProblem>(&bytes) {
|
match serde_json::from_slice::<HttpApiProblem>(&bytes) {
|
||||||
Ok(api_problem) => {
|
Ok(api_problem) => {
|
||||||
let detail = api_problem.detail.unwrap_or("".to_string());
|
let detail = api_problem.detail.unwrap_or("".to_string());
|
||||||
error!("Server {} error: {}. {}", status, api_problem.title, detail);
|
error!(
|
||||||
|
"Server {}: {}. {}",
|
||||||
|
status.as_u16(),
|
||||||
|
api_problem.title,
|
||||||
|
detail
|
||||||
|
);
|
||||||
Err(anyhow!(format!(
|
Err(anyhow!(format!(
|
||||||
"Server {} error: {}. {}",
|
"Server {}: {}. {}",
|
||||||
status, api_problem.title, detail
|
status.as_u16(),
|
||||||
|
api_problem.title,
|
||||||
|
detail
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let detail = str::from_utf8(&bytes).unwrap_or("unknown");
|
let detail = str::from_utf8(&bytes).unwrap_or("unknown");
|
||||||
error!("Server {} error: {}", status, detail);
|
error!("Server {}: {}", status.as_u16(), detail);
|
||||||
Err(anyhow!(format!("Server {} error: {}", status, detail)))
|
Err(anyhow!(format!("Server {}: {}", status.as_u16(), detail)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,7 +305,15 @@ mod tests {
|
|||||||
fn test_create_transaction_server_error() {
|
fn test_create_transaction_server_error() {
|
||||||
let mock = mock("POST", "/v1/transactions")
|
let mock = mock("POST", "/v1/transactions")
|
||||||
.with_status(500)
|
.with_status(500)
|
||||||
.with_body("Internal Server Error")
|
.with_header("content-type", "application/problem+json")
|
||||||
|
.with_body(
|
||||||
|
r#"{
|
||||||
|
"detail": "Some error detail",
|
||||||
|
"instance": "https://httpstatuses.com/500",
|
||||||
|
"status": 500,
|
||||||
|
"title": "Internal Server Error"
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
let api_url = CString::new("url").unwrap().into_raw();
|
let api_url = CString::new("url").unwrap().into_raw();
|
||||||
@ -323,7 +343,7 @@ mod tests {
|
|||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||||
"expected value at line 1 column 1"
|
"Server 500: Internal Server Error. Some error detail"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user