Replace error_status() method by several response types
This commit is contained in:
parent
5c20b27f35
commit
329a92d593
2 changed files with 114 additions and 44 deletions
|
@ -3,14 +3,11 @@
|
||||||
//! Handlers for viewing a list of existing customers or creating a new one.
|
//! Handlers for viewing a list of existing customers or creating a new one.
|
||||||
|
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use rocket::http::Status;
|
use rocket::{delete, get, post, put, uri};
|
||||||
use rocket::response::status::Created;
|
|
||||||
use rocket::uri;
|
|
||||||
use rocket::{delete, get, post, put};
|
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
use crate::models::{Customer, NewCustomer};
|
use crate::models::{Customer, NewCustomer};
|
||||||
use crate::rest_helpers::error_status;
|
use crate::rest_helpers::*;
|
||||||
use crate::DbConn;
|
use crate::DbConn;
|
||||||
|
|
||||||
pub mod invoices;
|
pub mod invoices;
|
||||||
|
@ -18,56 +15,42 @@ pub mod tasks;
|
||||||
|
|
||||||
/// Shows the list of customers.
|
/// Shows the list of customers.
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn index(conn: DbConn) -> Result<Json<Vec<Customer>>, Status> {
|
pub fn index(conn: DbConn) -> JsonResponse<Vec<Customer>> {
|
||||||
all!(Customer, *conn)
|
all!(Customer, *conn).into_json_response()
|
||||||
.map(|customers| Json(customers))
|
|
||||||
.map_err(|e| error_status(e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a customer from the new customer
|
/// Creates a customer from the new customer
|
||||||
#[post("/", format = "json", data = "<new_customer>")]
|
#[post("/", format = "json", data = "<new_customer>")]
|
||||||
pub fn create(
|
pub fn create(new_customer: Json<NewCustomer>, conn: DbConn) -> CreatedJsonResponse<Customer> {
|
||||||
new_customer: Json<NewCustomer>,
|
let new_customer = new_customer.into_inner();
|
||||||
conn: DbConn,
|
create!(Customer, new_customer, *conn).into_created_json_response(|customer: &Customer| {
|
||||||
) -> Result<Created<Json<Customer>>, Status> {
|
uri!("/customers", show: customer.id).to_string()
|
||||||
create!(Customer, new_customer.into_inner(), *conn)
|
|
||||||
.map(|customer: Customer| {
|
|
||||||
Created(
|
|
||||||
uri!("/customers", show: customer.id).to_string(),
|
|
||||||
Some(Json(customer)),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.map_err(|e| error_status(e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides the form for the data required to create a new customer.
|
/// Provides the form for the data required to create a new customer.
|
||||||
#[get("/new")]
|
#[get("/new")]
|
||||||
pub fn new() -> Json<NewCustomer> {
|
pub fn new() -> JsonResponse<NewCustomer> {
|
||||||
Json(NewCustomer::default())
|
NewCustomer::default().into_json_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a form for viewing and updating information of the customer with the given ID.
|
/// Shows a form for viewing and updating information of the customer with the given ID.
|
||||||
#[get("/<id>")]
|
#[get("/<id>")]
|
||||||
pub fn show(id: i32, conn: DbConn) -> Result<Json<Customer>, Status> {
|
pub fn show(id: i32, conn: DbConn) -> JsonResponse<Customer> {
|
||||||
get!(Customer, id, *conn)
|
get!(Customer, id, *conn).into_json_response()
|
||||||
.map(|customer| Json(customer))
|
|
||||||
.map_err(|e| error_status(e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the customer with the given ID.
|
/// Updates the customer with the given ID.
|
||||||
#[put("/<id>", format = "json", data = "<customer>")]
|
#[put("/<id>", format = "json", data = "<customer>")]
|
||||||
pub fn update(id: i32, customer: Json<Customer>, conn: DbConn) -> Result<Json<Customer>, Status> {
|
pub fn update(id: i32, customer: Json<Customer>, conn: DbConn) -> JsonResponse<Customer> {
|
||||||
update!(Customer, id, customer.into_inner(), *conn)
|
let customer = customer.into_inner();
|
||||||
.map(|customer| Json(customer))
|
update!(Customer, id, customer, *conn).into_json_response()
|
||||||
.map_err(|e| error_status(e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroys the customer with the given ID and redirects to the index handler.
|
/// Destroys the customer with the given ID and redirects to the index handler.
|
||||||
#[delete("/<id>")]
|
#[delete("/<id>")]
|
||||||
pub fn destroy(id: i32, conn: DbConn) -> Result<Status, Status> {
|
pub fn destroy(id: i32, conn: DbConn) -> NoContentResponse {
|
||||||
delete!(Customer, id, *conn)
|
destroy!(Customer, id, *conn).into_no_content_response()
|
||||||
.map(|_| Status::NoContent)
|
|
||||||
.map_err(|e| error_status(e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,12 +1,99 @@
|
||||||
use diesel::result::Error as QueryError;
|
//! Some REST API herlpers
|
||||||
use rocket::http::Status;
|
//!
|
||||||
|
//! The macros, types and traits in this module are used to create a simple REST API on top of
|
||||||
|
//! models that are in the database.
|
||||||
|
|
||||||
/// Returns a Rocket status for the provided Diesel (query) error.
|
#![allow(unused_macros)]
|
||||||
pub fn error_status(error: QueryError) -> Status {
|
|
||||||
dbg!(&error);
|
use diesel::result::{DatabaseErrorKind, Error as QueryError};
|
||||||
match error {
|
use rocket::http::Status;
|
||||||
|
use rocket::response::status::Created;
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Conversion into a Rocket [`Status`].
|
||||||
|
trait IntoStatus {
|
||||||
|
fn into_status(self) -> Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoStatus for QueryError {
|
||||||
|
fn into_status(self) -> Status {
|
||||||
|
match self {
|
||||||
QueryError::NotFound => Status::NotFound,
|
QueryError::NotFound => Status::NotFound,
|
||||||
|
QueryError::DatabaseError(kind, _info) => match kind {
|
||||||
|
DatabaseErrorKind::UniqueViolation => Status::Conflict,
|
||||||
|
DatabaseErrorKind::ForeignKeyViolation => Status::BadRequest,
|
||||||
_ => Status::InternalServerError,
|
_ => Status::InternalServerError,
|
||||||
|
},
|
||||||
|
_ => Status::InternalServerError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A specialized `Result` type for Rocket JSON reponses.
|
||||||
|
///
|
||||||
|
/// It contains either a JSON serialization or an (error) Rocket [`Status`].
|
||||||
|
pub type JsonResponse<T> = Result<Json<T>, Status>;
|
||||||
|
|
||||||
|
/// Conversion into a [`JsonResponse`].
|
||||||
|
pub trait IntoJsonResponse<T> {
|
||||||
|
fn into_json_response(self) -> JsonResponse<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoJsonResponse<T> for Result<T, QueryError> {
|
||||||
|
fn into_json_response(self) -> JsonResponse<T> {
|
||||||
|
self.map(|item| Json(item))
|
||||||
|
.map_err(|error| error.into_status())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoJsonResponse<T> for T
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
fn into_json_response(self) -> JsonResponse<T> {
|
||||||
|
Ok(Json(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A specialized `Result` type for Rocket Created (with JSON) reponses.
|
||||||
|
///
|
||||||
|
/// It contains either a JSON serialization with URI in a [`Created`] struct or an (error) Rocket
|
||||||
|
/// [`Status`].
|
||||||
|
pub type CreatedJsonResponse<T> = Result<Created<Json<T>>, Status>;
|
||||||
|
|
||||||
|
/// Conversion into a [`CreatedJsonResponse`].
|
||||||
|
pub trait IntoCreatedJsonResponse<T> {
|
||||||
|
fn into_created_json_response<F>(self, uri_func: F) -> CreatedJsonResponse<T>
|
||||||
|
where
|
||||||
|
F: Fn(&T) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoCreatedJsonResponse<T> for Result<T, QueryError> {
|
||||||
|
fn into_created_json_response<F>(self, uri_func: F) -> CreatedJsonResponse<T>
|
||||||
|
where
|
||||||
|
F: Fn(&T) -> String,
|
||||||
|
{
|
||||||
|
self.map(|item| Created(uri_func(&item), Some(Json(item))))
|
||||||
|
.map_err(|error| error.into_status())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A specialized `Result` type for Rocket `Status::NoContent` reponses.
|
||||||
|
///
|
||||||
|
/// It contains either a [`Status::NoContent`] in the Ok case, or an (error) Rocket [`Status`]
|
||||||
|
/// in the Err case.
|
||||||
|
pub type NoContentResponse = Result<Status, Status>;
|
||||||
|
|
||||||
|
/// Conversion into a [`NoContentResponse`].
|
||||||
|
pub trait IntoNoContentResponse {
|
||||||
|
fn into_no_content_response(self) -> NoContentResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoNoContentResponse for Result<T, QueryError> {
|
||||||
|
fn into_no_content_response(self) -> NoContentResponse {
|
||||||
|
self.map(|_item| Status::NoContent)
|
||||||
|
.map_err(|error| error.into_status())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +138,7 @@ macro_rules! update {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! delete {
|
macro_rules! destroy {
|
||||||
($model_name:ident, $primary_key:expr, $conn:expr) => {{
|
($model_name:ident, $primary_key:expr, $conn:expr) => {{
|
||||||
use diesel::associations::HasTable;
|
use diesel::associations::HasTable;
|
||||||
let table = $crate::models::$model_name::table();
|
let table = $crate::models::$model_name::table();
|
||||||
|
|
Loading…
Reference in a new issue