Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Initial attempt at making map_api_response accept non-json api responses #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/response/apifail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ impl fmt::Display for ApiError {

pub trait ApiResult: DeserializeOwned + Debug {}

// Ensure that even a raw text response body can be an ApiResult (this takes advantage
// of the fact that Serde can deserialize strings into... strings).
impl ApiResult for String {}


#[derive(Debug)]
pub enum ApiFailure {
Expand Down
27 changes: 25 additions & 2 deletions src/response/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ pub struct ApiSuccess<ResultType> {
pub errors: Vec<ApiError>,
}

// todo(gabbi): OR we can make this a Result<&dyn some_trait, ApiFailure>?
// Where this trait exposes accessor methods for two different ApiSuccess structs
// (ApiJsonSuccess and ApiTextSuccess)? This would be a massive breaking change, though.
// Users of cloudflare-rs already regularly directly access the .result field in ApiSuccess.
pub type ApiResponse<ResultType> = Result<ApiSuccess<ResultType>, ApiFailure>;

// If the response is 200 and parses, return Success.
Expand All @@ -22,12 +26,31 @@ pub fn map_api_response<ResultType: ApiResult>(
mut resp: reqwest::Response,
) -> ApiResponse<ResultType> {
if resp.status() == reqwest::StatusCode::OK {
let parsed: Result<ApiSuccess<ResultType>, reqwest::Error> = resp.json();

// todo(gabbi): Why can't I get resp.headers().get::<ContentType> to work below??
let content_type = resp.headers().get("content-type");
let parsed: Result<ApiSuccess<ResultType>, reqwest::Error> = match content_type {
Some(content_type) if content_type == "octet/string" => {
// Questionable but maybe workable solution below: take the text from a raw
// text response and put it in the result field of the ApiSuccess struct..?
let body = resp.text().unwrap();
let butt: ApiSuccess<ResultType> = ApiSuccess {
result: body,
result_info: None,
errors: vec![],
messages: json!(null),
};
Ok(butt)
},
None => resp.json(), // Default to json parsing.
};

// let parsed: Result<ApiSuccess<ResultType>, reqwest::Error> = response_body;
match parsed {
Ok(api_resp) => Ok(api_resp),
Err(e) => Err(ApiFailure::Invalid(e)),
}
} else {
} else { // oddly enough, even if workers KV success responses are raw text, the errors are in json :o
let parsed: Result<ApiErrors, reqwest::Error> = resp.json();
let errors = parsed.unwrap_or_default();
Err(ApiFailure::Error(resp.status(), errors))
Expand Down
2 changes: 2 additions & 0 deletions src/workerskv/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::response::ApiResult;
use chrono::offset::Utc;
use chrono::DateTime;
use serde::ser::{Serialize, Serializer};

pub mod create_namespace;
pub mod list_namespace_keys;
pub mod list_namespaces;
pub mod remove_namespace;
pub mod rename_namespace;
pub mod get_value;

/// Workers KV Namespace
/// A Namespace is a collection of key-value pairs stored in Workers KV.
Expand Down