Page MenuHomeDevCentral

No OneTemporary

diff --git a/Cargo.toml b/Cargo.toml
index 599ed9e..7396803 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,35 +1,35 @@
[package]
name = "limiting-factor"
-version = "0.3.0"
+version = "0.4.0"
authors = [
"Sébastien Santoro <dereckson@espace-win.org>",
]
description = "Library to create a REST API with Diesel and Rocket"
readme = "README.md"
keywords = [
"Diesel",
"API",
"Rocket",
"REST",
]
categories = [
"web-programming",
]
license = "BSD-2-Clause"
repository = "https://devcentral.nasqueron.org/source/limiting-factor/"
[dependencies]
diesel = { version = "^1.0.0", features = ["postgres", "r2d2", "chrono"], optional = true }
dotenv = "0.9.0"
log = "^0.4.4"
r2d2 = { version = "^0.8.2", optional = true }
rocket = "^0.3.16"
rocket_contrib = { version = "^0.3.16", features = [ "json" ] }
serde = { version = "1.0", optional = true }
[features]
default = ["pgsql", "serialization"]
minimal = ["serialization"]
pgsql = ["diesel", "r2d2"]
serialization = ["serde"]
diff --git a/src/api/replies.rs b/src/api/replies.rs
index c4c8791..0e291ce 100644
--- a/src/api/replies.rs
+++ b/src/api/replies.rs
@@ -1,161 +1,169 @@
//! # API standard and JSON responses.
//!
//! This module provides useful traits and methods to craft API replies from an existing type.
#[cfg(feature = "pgsql")]
use diesel::result::{DatabaseErrorInformation, DatabaseErrorKind, QueryResult};
#[cfg(feature = "pgsql")]
use diesel::result::Error as ResultError;
use rocket::http::Status;
use rocket::response::Failure;
use rocket_contrib::Json;
#[cfg(feature = "serialization")]
use serde::Serialize;
#[cfg(feature = "pgsql")]
use std::error::Error;
/* -------------------------------------------------------------
Custom types
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pub type ApiJsonResponse<T> = Result<Json<T>, Failure>;
/* -------------------------------------------------------------
API Response
:: Implementation for QueryResult (Diesel ORM)
:: Implementation for Json (Rocket contrib)
:: Implementation for Serialize (Serde)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/// This trait allows to consume an object into an HTTP response.
pub trait ApiResponse<T> {
/// Consumes the value and creates a JSON or a Failure result response.
fn into_json_response(self) -> ApiJsonResponse<T>;
}
#[cfg(feature = "pgsql")]
impl<T> ApiResponse<T> for QueryResult<T> {
/// Prepares an API response from a query result.
///
/// The result is the data structure prepared by the Diesel ORM after a SELECT query
/// with one result, for example using `first` method. You can also you use it to
/// parse the returning result (... RETURNING *), which is a default for Diesel after
/// an INSERT query.
///
/// So result can be:
/// - Ok(T)
/// - Err(any database error)
///
/// # Examples
///
/// To offer a /player/foo route to serve player information from the player table:
///
/// ```
/// use limiting_factor::api::ApiResponse;
/// use limiting_factor::api::ApiJsonResponse;
///
/// #[get("/player/<name>")]
/// pub fn get_player(connection: DatabaseConnection, name: String) -> ApiJsonResponse<Player> {
/// players
/// .filter(username.eq(&name))
/// .first::<Player>(&*connection)
/// .into_json_response()
/// }
/// ```
///
/// This will produce a JSON representation when the result is found,
/// a 404 error when no result is found, a 500 error if there is a database issue.
fn into_json_response(self) -> ApiJsonResponse<T> {
match self {
// CASE I - The query returns one value, we return a JSON representation fo the item
Ok(item) => Ok(Json(item)),
Err(error) => match error {
// Case II - The query returns no result, we return a 404 Not found response
ResultError::NotFound => Err(Failure::from(Status::NotFound)),
// Case III - We need to handle a database error, which could be a 400/409/500
ResultError::DatabaseError(kind, details) => Err(build_database_error_response(kind, details)),
// Case IV - The error is probably server responsbility, log it and throw a 500
_ => Err(error.into_failure_response()),
}
}
}
}
/// Prepares an API response from a JSON.
impl<T> ApiResponse<T> for Json<T> {
fn into_json_response(self) -> ApiJsonResponse<T> {
Ok(self)
}
}
/// Prepares an API response from a Serde-serializable result.
///
/// This is probably the easiest way to convert most struct
/// into API responders.
///
/// # Examples
///
#[cfg(feature = "serialization")]
impl<T> ApiResponse<T> for T
where T: Serialize
{
fn into_json_response(self) -> ApiJsonResponse<T> {
Ok(Json(self))
}
}
/* -------------------------------------------------------------
Failure response
:: Implementation for diesel::result::Error
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/// This trait allows to consume an object into an HTTP failure response.
pub trait FailureResponse {
/// Consumes the variable and creates a Failure response .
fn into_failure_response(self) -> Failure;
}
#[cfg(feature = "pgsql")]
impl FailureResponse for ResultError {
/// Consumes the error and creates a Failure 500 Internal server error response.
fn into_failure_response(self) -> Failure {
build_internal_server_error_response(self.description())
}
}
/* -------------------------------------------------------------
Helper methods to prepare API responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+pub fn build_not_found_response() -> Failure {
+ Failure::from(Status::NotFound)
+}
+
+pub fn build_bad_request_response() -> Failure {
+ Failure::from(Status::BadRequest)
+}
+
pub fn build_internal_server_error_response(message: &str) -> Failure {
warn!(target:"api", "{}", message);
Failure::from(Status::InternalServerError)
}
#[cfg(feature = "pgsql")]
fn build_database_error_response(error_kind: DatabaseErrorKind, info: Box<dyn DatabaseErrorInformation>) -> Failure {
match error_kind {
// Case IIIa - The query tries to do an INSERT violating an unique constraint
// e.g. two INSERT with the same unique value
// We return a 409 Conflict
DatabaseErrorKind::UniqueViolation => Failure::from(Status::Conflict),
// Case IIIb - The query violated a foreign key constraint
// e.g. an INSERT referring to a non existing user 1004
// when there is no id 1004 in users table
// We return a 400 Bad request
DatabaseErrorKind::ForeignKeyViolation => Failure::from(Status::BadRequest),
// Case IIIc - For other databases errors, the client responsibility isn't involved.
_ => build_internal_server_error_response(info.message()),
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Sep 15, 07:22 (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2983855
Default Alt Text
(7 KB)

Event Timeline