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.
|
||||
|
||||
use diesel::prelude::*;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::status::Created;
|
||||
use rocket::uri;
|
||||
use rocket::{delete, get, post, put};
|
||||
use rocket::{delete, get, post, put, uri};
|
||||
use rocket_contrib::json::Json;
|
||||
|
||||
use crate::models::{Customer, NewCustomer};
|
||||
use crate::rest_helpers::error_status;
|
||||
use crate::rest_helpers::*;
|
||||
use crate::DbConn;
|
||||
|
||||
pub mod invoices;
|
||||
|
@ -18,56 +15,42 @@ pub mod tasks;
|
|||
|
||||
/// Shows the list of customers.
|
||||
#[get("/")]
|
||||
pub fn index(conn: DbConn) -> Result<Json<Vec<Customer>>, Status> {
|
||||
all!(Customer, *conn)
|
||||
.map(|customers| Json(customers))
|
||||
.map_err(|e| error_status(e))
|
||||
pub fn index(conn: DbConn) -> JsonResponse<Vec<Customer>> {
|
||||
all!(Customer, *conn).into_json_response()
|
||||
}
|
||||
|
||||
/// Creates a customer from the new customer
|
||||
#[post("/", format = "json", data = "<new_customer>")]
|
||||
pub fn create(
|
||||
new_customer: Json<NewCustomer>,
|
||||
conn: DbConn,
|
||||
) -> Result<Created<Json<Customer>>, Status> {
|
||||
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))
|
||||
pub fn create(new_customer: Json<NewCustomer>, conn: DbConn) -> CreatedJsonResponse<Customer> {
|
||||
let new_customer = new_customer.into_inner();
|
||||
create!(Customer, new_customer, *conn).into_created_json_response(|customer: &Customer| {
|
||||
uri!("/customers", show: customer.id).to_string()
|
||||
})
|
||||
}
|
||||
|
||||
/// Provides the form for the data required to create a new customer.
|
||||
#[get("/new")]
|
||||
pub fn new() -> Json<NewCustomer> {
|
||||
Json(NewCustomer::default())
|
||||
pub fn new() -> JsonResponse<NewCustomer> {
|
||||
NewCustomer::default().into_json_response()
|
||||
}
|
||||
|
||||
/// Shows a form for viewing and updating information of the customer with the given ID.
|
||||
#[get("/<id>")]
|
||||
pub fn show(id: i32, conn: DbConn) -> Result<Json<Customer>, Status> {
|
||||
get!(Customer, id, *conn)
|
||||
.map(|customer| Json(customer))
|
||||
.map_err(|e| error_status(e))
|
||||
pub fn show(id: i32, conn: DbConn) -> JsonResponse<Customer> {
|
||||
get!(Customer, id, *conn).into_json_response()
|
||||
}
|
||||
|
||||
/// Updates the customer with the given ID.
|
||||
#[put("/<id>", format = "json", data = "<customer>")]
|
||||
pub fn update(id: i32, customer: Json<Customer>, conn: DbConn) -> Result<Json<Customer>, Status> {
|
||||
update!(Customer, id, customer.into_inner(), *conn)
|
||||
.map(|customer| Json(customer))
|
||||
.map_err(|e| error_status(e))
|
||||
pub fn update(id: i32, customer: Json<Customer>, conn: DbConn) -> JsonResponse<Customer> {
|
||||
let customer = customer.into_inner();
|
||||
update!(Customer, id, customer, *conn).into_json_response()
|
||||
}
|
||||
|
||||
/// Destroys the customer with the given ID and redirects to the index handler.
|
||||
#[delete("/<id>")]
|
||||
pub fn destroy(id: i32, conn: DbConn) -> Result<Status, Status> {
|
||||
delete!(Customer, id, *conn)
|
||||
.map(|_| Status::NoContent)
|
||||
.map_err(|e| error_status(e))
|
||||
pub fn destroy(id: i32, conn: DbConn) -> NoContentResponse {
|
||||
destroy!(Customer, id, *conn).into_no_content_response()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,12 +1,99 @@
|
|||
use diesel::result::Error as QueryError;
|
||||
use rocket::http::Status;
|
||||
//! Some REST API herlpers
|
||||
//!
|
||||
//! 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.
|
||||
pub fn error_status(error: QueryError) -> Status {
|
||||
dbg!(&error);
|
||||
match error {
|
||||
QueryError::NotFound => Status::NotFound,
|
||||
_ => Status::InternalServerError,
|
||||
#![allow(unused_macros)]
|
||||
|
||||
use diesel::result::{DatabaseErrorKind, Error as QueryError};
|
||||
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::DatabaseError(kind, _info) => match kind {
|
||||
DatabaseErrorKind::UniqueViolation => Status::Conflict,
|
||||
DatabaseErrorKind::ForeignKeyViolation => Status::BadRequest,
|
||||
_ => 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) => {{
|
||||
use diesel::associations::HasTable;
|
||||
let table = $crate::models::$model_name::table();
|
||||
|
|
Loading…
Reference in a new issue