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",
|
"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]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@ -643,6 +658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -651,6 +667,17 @@ version = "0.3.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
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]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@ -683,6 +710,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@ -695,9 +728,13 @@ version = "0.3.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite 0.2.9",
|
"pin-project-lite 0.2.9",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
@ -867,8 +904,10 @@ name = "inventory-system"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
|
"async-trait",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -9,7 +9,9 @@ edition = "2021"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
||||||
|
futures = "0.3.21"
|
||||||
async-std = "1"
|
async-std = "1"
|
||||||
|
async-trait = "0.1.56"
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tide = "0.17.0-beta.1"
|
tide = "0.17.0-beta.1"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::State;
|
use super::{ApiEndpoint, ApiResult, ApiSuccess, State};
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -8,7 +8,7 @@ use serde::Serialize;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub fn setup_routes(mut route: tide::Route<Arc<State>>) {
|
pub fn setup_routes(mut route: tide::Route<Arc<State>>) {
|
||||||
route.at("/").get(index);
|
route.at("/").get(ApiEndpoint(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Serialize)]
|
#[derive(Queryable, Serialize)]
|
||||||
@ -18,16 +18,13 @@ pub struct Container {
|
|||||||
pub name: String,
|
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 db::schema::containers::dsl::*;
|
||||||
use diesel::{QueryDsl, RunQueryDsl};
|
use diesel::{QueryDsl, RunQueryDsl};
|
||||||
|
|
||||||
let mut connection = req.state().db.get().unwrap();
|
let mut connection = req.state().db.get().unwrap();
|
||||||
|
|
||||||
let items: Vec<Container> = containers
|
let items: Vec<Container> = containers.limit(5).load::<Container>(&mut connection)?;
|
||||||
.limit(5)
|
|
||||||
.load::<Container>(&mut connection)
|
|
||||||
.expect("Error loading containers");
|
|
||||||
|
|
||||||
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 containers;
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
use error::{ApiEndpoint, ApiResult, ApiSuccess};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user