From 4eca618b77a3e961c7891d425f28fadb5f0f862f Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Fri, 14 Jan 2022 17:13:44 +0000 Subject: [PATCH] Add failure recovery to web package --- src/cnf/mod.rs | 2 + src/err/mod.rs | 2 + src/web/fail.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ src/web/key.rs | 97 +++++++++++++++++++++++++++++++++++-------- src/web/mod.rs | 3 ++ src/web/root.rs | 5 +-- src/web/sql.rs | 12 +++--- 7 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 src/web/fail.rs diff --git a/src/cnf/mod.rs b/src/cnf/mod.rs index e13970e6..fd149fac 100644 --- a/src/cnf/mod.rs +++ b/src/cnf/mod.rs @@ -1,2 +1,4 @@ // Specifies how many subqueries will be processed recursively before the query fails. pub const MAX_RECURSIVE_QUERIES: usize = 16; + +pub const APP_ENDPOINT: &'static str = "https://app.surrealdb.com"; diff --git a/src/err/mod.rs b/src/err/mod.rs index c93933eb..98a8eba7 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -147,3 +147,5 @@ pub enum Error { #[error("CBOR Error: {0}")] CborError(#[from] CborError), } + +impl warp::reject::Reject for Error {} diff --git a/src/web/fail.rs b/src/web/fail.rs new file mode 100644 index 00000000..80d0d66c --- /dev/null +++ b/src/web/fail.rs @@ -0,0 +1,108 @@ +use crate::err::Error; +use serde::Serialize; +use warp::http::StatusCode; + +#[derive(Serialize)] +struct Message { + code: u16, + #[serde(skip_serializing_if = "Option::is_none")] + details: Option, + #[serde(skip_serializing_if = "Option::is_none")] + description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + information: Option, +} + +pub async fn recover(err: warp::Rejection) -> Result { + if err.is_not_found() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 404, + details: Some(format!("Requested resource not found")), + description: Some(format!("The requested resource does not exist. Check that you have entered the url correctly.")), + information: None, + }), + StatusCode::NOT_FOUND, + )) + } else if let Some(err) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 400, + details: Some(format!("Request problems detected")), + description: Some(format!("There is a problem with your request. Refer to the documentation for further information.")), + information: Some(err.to_string()), + }), + StatusCode::BAD_REQUEST, + )) + } else if let Some(_) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 405, + details: Some(format!("Request content length too large")), + description: Some(format!("The requested http method is not allowed for this resource. Refer to the documentation for allowed methods.")), + information: None, + }), + StatusCode::METHOD_NOT_ALLOWED, + )) + } else if let Some(_) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 413, + details: Some(format!("Request problems detected")), + description: Some(format!("The request has exceeded the maximum payload size. Refer to the documentation for the request limitations.")), + information: None, + }), + StatusCode::PAYLOAD_TOO_LARGE, + )) + } else if let Some(_) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 415, + details: Some(format!("Unsupported content type requested")), + description: Some(format!("The request needs to adhere to certain constraints. Refer to the documentation for supported content types.")), + information: None, + }), + StatusCode::UNSUPPORTED_MEDIA_TYPE, + )) + } else if let Some(_) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 412, + details: Some(format!("Request problems detected")), + description: Some(format!("The request appears to be missing a required header. Refer to the documentation for request requirements.")), + information: None, + }), + StatusCode::PRECONDITION_FAILED, + )) + } else if let Some(_) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 501, + details: Some(format!("Not Implemented")), + description: Some(format!("The server either does not recognize the request method, or it lacks the ability to fulfill the request.")), + information: None, + }), + StatusCode::NOT_IMPLEMENTED, + )) + } else if let Some(_) = err.find::() { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 501, + details: Some(format!("Not Implemented")), + description: Some(format!("The server either does not recognize the request method, or it lacks the ability to fulfill the request.")), + information: None, + }), + StatusCode::NOT_IMPLEMENTED, + )) + } else { + Ok(warp::reply::with_status( + warp::reply::json(&Message { + code: 500, + details: Some(format!("Internal server error")), + description: Some(format!("There was a problem with our servers, and we have been notified. Refer to the documentation for further information")), + information: None, + }), + StatusCode::INTERNAL_SERVER_ERROR, + )) + } +} diff --git a/src/web/key.rs b/src/web/key.rs index 1e53b5e6..44c05937 100644 --- a/src/web/key.rs +++ b/src/web/key.rs @@ -32,6 +32,8 @@ pub fn config() -> impl Filter(http::header::CONTENT_TYPE.as_str())); // Set base path for all let base = base.and(path!("key" / String).and(warp::path::end())); // Set select method @@ -55,6 +57,8 @@ pub fn config() -> impl Filter(http::header::CONTENT_TYPE.as_str())); // Set base path for one let base = base.and(path!("key" / String / String).and(warp::path::end())); // Set select method @@ -96,6 +100,7 @@ pub fn config() -> impl Filter Result { @@ -106,12 +111,19 @@ async fn select_all( ); let mut vars = HashMap::new(); vars.insert(String::from("table"), Value::from(table)); - let res = crate::dbs::execute(sql.as_str(), session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql.as_str(), session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } async fn create_all( session: Session, + output: String, table: String, body: Bytes, ) -> Result { @@ -122,19 +134,35 @@ async fn create_all( let mut vars = HashMap::new(); vars.insert(String::from("table"), Value::from(table)); vars.insert(String::from("data"), Value::from(data)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } Err(_) => todo!(), } } -async fn delete_all(session: Session, table: String) -> Result { +async fn delete_all( + session: Session, + output: String, + table: String, +) -> Result { let sql = "DELETE type::table($table)"; let mut vars = HashMap::new(); vars.insert(String::from("table"), Value::from(table)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } // ------------------------------ @@ -143,6 +171,7 @@ async fn delete_all(session: Session, table: String) -> Result Result { @@ -150,12 +179,19 @@ async fn select_one( let mut vars = HashMap::new(); vars.insert(String::from("table"), Value::from(table)); vars.insert(String::from("id"), Value::from(id)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } async fn create_one( session: Session, + output: String, table: String, id: String, body: Bytes, @@ -168,8 +204,14 @@ async fn create_one( vars.insert(String::from("table"), Value::from(table)); vars.insert(String::from("id"), Value::from(id)); vars.insert(String::from("data"), Value::from(data)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } Err(_) => todo!(), } @@ -177,6 +219,7 @@ async fn create_one( async fn update_one( session: Session, + output: String, table: String, id: String, body: Bytes, @@ -189,8 +232,14 @@ async fn update_one( vars.insert(String::from("table"), Value::from(table)); vars.insert(String::from("id"), Value::from(id)); vars.insert(String::from("data"), Value::from(data)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } Err(_) => todo!(), } @@ -198,6 +247,7 @@ async fn update_one( async fn modify_one( session: Session, + output: String, table: String, id: String, body: Bytes, @@ -210,8 +260,14 @@ async fn modify_one( vars.insert(String::from("table"), Value::from(table)); vars.insert(String::from("id"), Value::from(id)); vars.insert(String::from("data"), Value::from(data)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } Err(_) => todo!(), } @@ -219,6 +275,7 @@ async fn modify_one( async fn delete_one( session: Session, + output: String, table: String, id: String, ) -> Result { @@ -226,6 +283,12 @@ async fn delete_one( let mut vars = HashMap::new(); vars.insert(String::from("table"), Value::from(table)); vars.insert(String::from("id"), Value::from(id)); - let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap(); - Ok(warp::reply::json(&res)) + match crate::dbs::execute(sql, session, Some(vars)).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), + } } diff --git a/src/web/mod.rs b/src/web/mod.rs index 141068c6..349cc681 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,5 +1,6 @@ mod conf; mod export; +mod fail; mod head; mod import; mod key; @@ -45,6 +46,8 @@ pub async fn init(conf: &clap::ArgMatches) -> Result<(), Error> { .or(sql::config()) // API query endpoint .or(key::config()) + // Catch all errors + .recover(fail::recover) // End routes setup ; diff --git a/src/web/root.rs b/src/web/root.rs index 0f1e1c44..150dd22b 100644 --- a/src/web/root.rs +++ b/src/web/root.rs @@ -1,8 +1,7 @@ +use crate::cnf; use warp::http::Uri; use warp::Filter; pub fn config() -> impl Filter + Clone { - warp::path::end() - .and(warp::get()) - .map(|| warp::redirect(Uri::from_static("https://app.surrealdb.com"))) + warp::path::end().and(warp::get()).map(|| warp::redirect(Uri::from_static(cnf::APP_ENDPOINT))) } diff --git a/src/web/sql.rs b/src/web/sql.rs index c87b1b86..5707f101 100644 --- a/src/web/sql.rs +++ b/src/web/sql.rs @@ -40,10 +40,12 @@ async fn handler( sql: Bytes, ) -> Result { let sql = std::str::from_utf8(&sql).unwrap(); - let res = crate::dbs::execute(sql, session, None).await.unwrap(); - match output.as_ref() { - "application/json" => Ok(warp::reply::json(&res)), - "application/cbor" => Ok(warp::reply::json(&res)), - _ => Err(warp::reject::not_found()), + match crate::dbs::execute(sql, session, None).await { + Ok(res) => match output.as_ref() { + "application/json" => Ok(warp::reply::json(&res)), + "application/cbor" => Ok(warp::reply::json(&res)), + _ => Err(warp::reject::not_found()), + }, + Err(err) => Err(warp::reject::custom(err)), } }