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

Support integer and TaggedBase64 request parameters #65

Merged
merged 2 commits into from
Aug 17, 2022
Merged
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ name = "hello-world"
test = true

[dependencies]
ark-serialize = "0.3.0"
async-std = { version = "1.8.0", features = ["attributes"] }
async-trait = "0.1.51"
bincode = "1.3.3"
Expand All @@ -23,6 +24,7 @@ edit-distance = "2.1.0"
futures = "0.3.21"
futures-util = "0.3.8"
http = "0.2.7"
jf-utils = { features = ["std"], git = "https://github.com/EspressoSystems/jellyfish.git", tag = "0.1.1" }
lazy_static = "1.4.0"
libc = "0.2.126"
markdown = "0.3"
Expand Down
2 changes: 1 addition & 1 deletion examples/hello-world/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async fn serve(port: u16) -> io::Result<()> {
async move {
let new_greeting = req.string_param("greeting")?;
info!("called /setgreeting with :greeting = {}", new_greeting);
*greeting = new_greeting;
*greeting = new_greeting.to_string();
Ok(())
}
.boxed()
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ pub fn check_literals(url: &Url, api: &Value, first_segment: &str) -> String {
let mut typos = String::new();
let meta = &api["meta"];
let api_map = api[ROUTE.as_ref()].as_table().unwrap();
api_map[&*first_segment][PATH.as_ref()]
api_map[first_segment][PATH.as_ref()]
.as_array()
.unwrap()
.iter()
Expand Down
118 changes: 106 additions & 12 deletions src/request.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use ark_serialize::CanonicalDeserialize;
use jf_utils::Tagged;
use serde::{Deserialize, Serialize};
use snafu::{OptionExt, Snafu};
use std::collections::HashMap;
use std::fmt::Display;
use strum_macros::EnumString;
use tagged_base64::TaggedBase64;
use tide::http::{content::Accept, Headers};

#[derive(Clone, Debug, Snafu)]
#[derive(Clone, Debug, Snafu, Deserialize, Serialize)]
pub enum RequestError {
#[snafu(display("missing required parameter: {}", name))]
MissingParam { name: String },
Expand Down Expand Up @@ -35,11 +38,20 @@ pub enum RequestError {
#[snafu(display("Unable to deserialize from bincode"))]
Bincode,

#[snafu(display("Unable to deserialize from ark format: {}", reason))]
ArkSerialize { reason: String },

#[snafu(display("Body type not specified or type not supported"))]
UnsupportedBody,

#[snafu(display("HTTP protocol error: {}", reason))]
Http { reason: String },

#[snafu(display("error parsing {} parameter: {}", param_type, reason))]
InvalidParam { param_type: String, reason: String },

#[snafu(display("unexpected tag in TaggedBase64: {} (expected {})", actual, expected))]
TagMismatch { actual: String, expected: String },
}

/// Parameters passed to a route handler.
Expand Down Expand Up @@ -259,7 +271,7 @@ impl RequestParams {
///
/// Like [param](Self::param), but returns [None] if the parameter value cannot be converted to
/// a [String].
pub fn string_param<Name>(&self, name: &Name) -> Result<String, RequestError>
pub fn string_param<Name>(&self, name: &Name) -> Result<&str, RequestError>
where
Name: ?Sized + Display,
{
Expand All @@ -272,6 +284,46 @@ impl RequestParams {
})
}

/// Get the value of a named parameter and convert it to [TaggedBase64].
///
/// Like [param](Self::param), but returns [None] if the parameter value cannot be converted to
/// [TaggedBase64].
pub fn tagged_base64_param<Name>(&self, name: &Name) -> Result<&TaggedBase64, RequestError>
where
Name: ?Sized + Display,
{
self.param(name).and_then(|val| {
val.as_tagged_base64().context(IncorrectParamTypeSnafu {
name: name.to_string(),
param_type: val.param_type(),
expected: "TaggedBase64".to_string(),
})
})
}

/// Get the value of a named parameter and convert it to a custom type through [TaggedBase64].
///
/// Like [param](Self::param), but returns [None] if the parameter value cannot be converted to
/// `T`.
pub fn blob_param<Name, T>(&self, name: &Name) -> Result<T, RequestError>
where
Name: ?Sized + Display,
T: Tagged + CanonicalDeserialize,
{
self.tagged_base64_param(name).and_then(|tb64| {
if tb64.tag() == T::tag() {
T::deserialize(&*tb64.value()).map_err(|source| RequestError::ArkSerialize {
reason: source.to_string(),
})
} else {
Err(RequestError::TagMismatch {
actual: tb64.tag(),
expected: T::tag(),
})
}
})
}

pub fn body_bytes(&self) -> Vec<u8> {
self.post_data.clone()
}
Expand Down Expand Up @@ -329,9 +381,36 @@ impl RequestParamValue {
RequestParamType::Literal => {
Ok(Some(RequestParamValue::Literal(param.to_string())))
}
_ => unimplemented!(
"parsing String into RequestParamValue based on formal.param_type"
),
RequestParamType::Boolean => {
Ok(Some(RequestParamValue::Boolean(param.parse().map_err(
|err: std::str::ParseBoolError| RequestError::InvalidParam {
param_type: "Boolean".to_string(),
reason: err.to_string(),
},
)?)))
}
RequestParamType::Integer => {
Ok(Some(RequestParamValue::Integer(param.parse().map_err(
|err: std::num::ParseIntError| RequestError::InvalidParam {
param_type: "Integer".to_string(),
reason: err.to_string(),
},
)?)))
}
RequestParamType::Hexadecimal => Ok(Some(RequestParamValue::Hexadecimal(
param.parse().map_err(|err: std::num::ParseIntError| {
RequestError::InvalidParam {
param_type: "Hexadecimal".to_string(),
reason: err.to_string(),
}
})?,
))),
RequestParamType::TaggedBase64 => Ok(Some(RequestParamValue::TaggedBase64(
TaggedBase64::parse(param).map_err(|err| RequestError::InvalidParam {
param_type: "TaggedBase64".to_string(),
reason: err.to_string(),
})?,
))),
}
} else {
unimplemented!("check for the parameter in the request body")
Expand All @@ -348,21 +427,36 @@ impl RequestParamValue {
}
}

pub fn as_string(&self) -> Option<String> {
pub fn as_string(&self) -> Option<&str> {
match self {
Self::Literal(s) => Some(s.clone()),
_ => {
unimplemented!("extracting a String from other parameter types, like TaggedBase64")
}
Self::Literal(s) => Some(s),
_ => None,
}
}

pub fn as_integer(&self) -> Option<u128> {
unimplemented!()
match self {
Self::Integer(x) | Self::Hexadecimal(x) => Some(*x),
_ => None,
}
}

pub fn as_boolean(&self) -> Option<bool> {
match self {
Self::Boolean(x) => Some(*x),
_ => None,
}
}

pub fn as_tagged_base64(&self) -> Option<&TaggedBase64> {
match self {
Self::TaggedBase64(x) => Some(x),
_ => None,
}
}
}

#[derive(Clone, Copy, Debug, EnumString, strum_macros::Display)]
#[derive(Clone, Copy, Debug, EnumString, strum_macros::Display, Deserialize, Serialize)]
pub enum RequestParamType {
Boolean,
Hexadecimal,
Expand Down