added mapped api response types
This commit is contained in:
parent
896898a216
commit
3a44c299f3
39
Cargo.lock
generated
39
Cargo.lock
generated
@ -636,6 +636,21 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.21"
|
||||
@ -643,6 +658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -651,6 +667,17 @@ version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.21"
|
||||
@ -683,6 +710,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.21"
|
||||
@ -695,9 +728,13 @@ version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite 0.2.9",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@ -867,8 +904,10 @@ name = "inventory-system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"diesel",
|
||||
"diesel_migrations",
|
||||
"futures",
|
||||
"log",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
|
@ -9,7 +9,9 @@ edition = "2021"
|
||||
log = "0.4"
|
||||
thiserror = "1"
|
||||
|
||||
futures = "0.3.21"
|
||||
async-std = "1"
|
||||
async-trait = "0.1.56"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tide = "0.17.0-beta.1"
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::State;
|
||||
use super::{ApiEndpoint, ApiResult, ApiSuccess, State};
|
||||
use crate::db;
|
||||
|
||||
use std::sync::Arc;
|
||||
@ -8,7 +8,7 @@ use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn setup_routes(mut route: tide::Route<Arc<State>>) {
|
||||
route.at("/").get(index);
|
||||
route.at("/").get(ApiEndpoint(index));
|
||||
}
|
||||
|
||||
#[derive(Queryable, Serialize)]
|
||||
@ -18,16 +18,13 @@ pub struct Container {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
async fn index(req: tide::Request<Arc<State>>) -> tide::Result {
|
||||
async fn index(req: tide::Request<Arc<State>>) -> ApiResult<Vec<Container>> {
|
||||
use db::schema::containers::dsl::*;
|
||||
use diesel::{QueryDsl, RunQueryDsl};
|
||||
|
||||
let mut connection = req.state().db.get().unwrap();
|
||||
|
||||
let items: Vec<Container> = containers
|
||||
.limit(5)
|
||||
.load::<Container>(&mut connection)
|
||||
.expect("Error loading containers");
|
||||
let items: Vec<Container> = containers.limit(5).load::<Container>(&mut connection)?;
|
||||
|
||||
Ok(tide::Body::from_json(&items).unwrap().into())
|
||||
Ok(ApiSuccess::Ok(items))
|
||||
}
|
||||
|
118
src/api/error.rs
Normal file
118
src/api/error.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use log::error;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use diesel::result::Error as DieselError;
|
||||
use futures::future::Future;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
use tide::{Body, Response, StatusCode};
|
||||
|
||||
pub type ApiResult<T> = Result<ApiSuccess<T>, ApiError>;
|
||||
|
||||
#[derive(Serialize, Error, Debug)]
|
||||
pub enum ApiSuccess<T> {
|
||||
Ok(T),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ApiError {
|
||||
#[error("not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("database error: {0}")]
|
||||
Database(DieselError),
|
||||
}
|
||||
|
||||
impl From<DieselError> for ApiError {
|
||||
fn from(err: DieselError) -> Self {
|
||||
match err {
|
||||
DieselError::NotFound => Self::NotFound,
|
||||
e => Self::Database(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ApiEndpoint<E>(pub(super) E);
|
||||
|
||||
#[async_trait]
|
||||
impl<E, S, F, T> tide::Endpoint<S> for ApiEndpoint<E>
|
||||
where
|
||||
E: Fn(tide::Request<S>) -> F + Send + Sync + 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
F: Future<Output = ApiResult<T>> + Send + Sync + 'static,
|
||||
T: Serialize,
|
||||
{
|
||||
async fn call(&self, req: tide::Request<S>) -> tide::Result {
|
||||
Ok(ApiResultProxy::<T>::from(self.0(req).await).into())
|
||||
}
|
||||
}
|
||||
|
||||
struct ApiResultProxy<T>(pub Result<ApiSuccess<T>, ApiError>);
|
||||
|
||||
impl<T> From<ApiResult<T>> for ApiResultProxy<T> {
|
||||
fn from(res: ApiResult<T>) -> Self {
|
||||
Self(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum ApiResponse<'a, T> {
|
||||
#[serde(rename = "content")]
|
||||
Content(&'a T),
|
||||
|
||||
#[serde(rename = "err")]
|
||||
Err(&'a str),
|
||||
}
|
||||
|
||||
impl<T: Serialize> From<ApiResultProxy<T>> for Response {
|
||||
fn from(res: ApiResultProxy<T>) -> Self {
|
||||
match res.0 {
|
||||
Ok(resp) => {
|
||||
let (status_code, body) = resp.response();
|
||||
|
||||
Response::builder(status_code)
|
||||
.body(
|
||||
Body::from_json(&ApiResponse::Content(body))
|
||||
.expect("json serialisation should succeed"),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
let (status_code, body) = err.response();
|
||||
|
||||
Response::builder(status_code)
|
||||
.body(
|
||||
Body::from_json(&ApiResponse::<()>::Err(&body))
|
||||
.expect("json serialisation should succeed"),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ApiSuccess<T> {
|
||||
fn response(&self) -> (StatusCode, &T) {
|
||||
match self {
|
||||
Self::Ok(b) => (StatusCode::Ok, b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiError {
|
||||
fn response(&self) -> (StatusCode, String) {
|
||||
match self {
|
||||
Self::NotFound => (StatusCode::NotFound, "requested object not found".into()),
|
||||
|
||||
e => {
|
||||
error!("internal server error handling request: {}", e);
|
||||
|
||||
(
|
||||
StatusCode::InternalServerError,
|
||||
"internal server error".into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
mod containers;
|
||||
mod error;
|
||||
|
||||
use error::{ApiEndpoint, ApiResult, ApiSuccess};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user