Add list_shops endpoint
This commit is contained in:
parent
28d6e50a5a
commit
6b1f31f246
94
src/main.rs
94
src/main.rs
@ -71,10 +71,12 @@ async fn main() -> Result<()> {
|
|||||||
let home = warp::path!("api" / "v1").map(|| "Shopkeeper home page");
|
let home = warp::path!("api" / "v1").map(|| "Shopkeeper home page");
|
||||||
let get_shop = filters::get_shop(env.clone());
|
let get_shop = filters::get_shop(env.clone());
|
||||||
let create_shop = filters::create_shop(env.clone());
|
let create_shop = filters::create_shop(env.clone());
|
||||||
|
let list_shops = filters::list_shops(env.clone());
|
||||||
let get_owner = filters::get_owner(env.clone());
|
let get_owner = filters::get_owner(env.clone());
|
||||||
let create_owner = filters::create_owner(env.clone());
|
let create_owner = filters::create_owner(env.clone());
|
||||||
let routes = create_shop
|
let routes = create_shop
|
||||||
.or(get_shop)
|
.or(get_shop)
|
||||||
|
.or(list_shops)
|
||||||
.or(create_owner)
|
.or(create_owner)
|
||||||
.or(get_owner)
|
.or(get_owner)
|
||||||
.or(home)
|
.or(home)
|
||||||
@ -106,7 +108,7 @@ mod filters {
|
|||||||
use warp::{Filter, Rejection, Reply};
|
use warp::{Filter, Rejection, Reply};
|
||||||
|
|
||||||
use super::handlers;
|
use super::handlers;
|
||||||
use super::models::{Owner, Shop};
|
use super::models::{ListParams, Owner, Shop};
|
||||||
use super::Environment;
|
use super::Environment;
|
||||||
|
|
||||||
pub fn get_shop(
|
pub fn get_shop(
|
||||||
@ -121,13 +123,23 @@ mod filters {
|
|||||||
pub fn create_shop(
|
pub fn create_shop(
|
||||||
env: Environment,
|
env: Environment,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
warp::path("shops")
|
warp::path!("shops")
|
||||||
.and(warp::post())
|
.and(warp::post())
|
||||||
.and(json_body::<Shop>())
|
.and(json_body::<Shop>())
|
||||||
.and(with_env(env))
|
.and(with_env(env))
|
||||||
.and_then(handlers::create_shop)
|
.and_then(handlers::create_shop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn list_shops(
|
||||||
|
env: Environment,
|
||||||
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
|
warp::path!("shops")
|
||||||
|
.and(warp::get())
|
||||||
|
.and(warp::query::<ListParams>())
|
||||||
|
.and(with_env(env))
|
||||||
|
.and_then(handlers::list_shops)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_owner(
|
pub fn get_owner(
|
||||||
env: Environment,
|
env: Environment,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
@ -140,7 +152,7 @@ mod filters {
|
|||||||
pub fn create_owner(
|
pub fn create_owner(
|
||||||
env: Environment,
|
env: Environment,
|
||||||
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
warp::path("owners")
|
warp::path!("owners")
|
||||||
.and(warp::post())
|
.and(warp::post())
|
||||||
.and(json_body::<Owner>())
|
.and(json_body::<Owner>())
|
||||||
.and(warp::addr::remote())
|
.and(warp::addr::remote())
|
||||||
@ -163,13 +175,14 @@ mod filters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod handlers {
|
mod handlers {
|
||||||
|
use http_api_problem::HttpApiProblem;
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use warp::http::StatusCode;
|
use warp::http::StatusCode;
|
||||||
use warp::reply::{json, with_header, with_status};
|
use warp::reply::{json, with_header, with_status};
|
||||||
use warp::{Rejection, Reply};
|
use warp::{Rejection, Reply};
|
||||||
|
|
||||||
use super::models::{Owner, Shop};
|
use super::models::{ListParams, Owner, Shop};
|
||||||
use super::problem::reject_anyhow;
|
use super::problem::reject_anyhow;
|
||||||
use super::Environment;
|
use super::Environment;
|
||||||
|
|
||||||
@ -180,6 +193,23 @@ mod handlers {
|
|||||||
Ok(reply)
|
Ok(reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn list_shops(
|
||||||
|
list_params: ListParams,
|
||||||
|
env: Environment,
|
||||||
|
) -> Result<impl Reply, Rejection> {
|
||||||
|
let shops = Shop::list(&env.db, list_params)
|
||||||
|
.await
|
||||||
|
.map_err(reject_anyhow)?;
|
||||||
|
if shops.is_empty() {
|
||||||
|
return Err(warp::reject::custom(
|
||||||
|
HttpApiProblem::with_title_and_type_from_status(StatusCode::NOT_FOUND),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let reply = json(&shops);
|
||||||
|
let reply = with_status(reply, StatusCode::OK);
|
||||||
|
Ok(reply)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_shop(shop: Shop, env: Environment) -> Result<impl Reply, Rejection> {
|
pub async fn create_shop(shop: Shop, env: Environment) -> Result<impl Reply, Rejection> {
|
||||||
let saved_shop = shop.save(&env.db).await.map_err(reject_anyhow)?;
|
let saved_shop = shop.save(&env.db).await.map_err(reject_anyhow)?;
|
||||||
let url = saved_shop.url(&env.api_url).map_err(reject_anyhow)?;
|
let url = saved_shop.url(&env.api_url).map_err(reject_anyhow)?;
|
||||||
@ -223,9 +253,46 @@ mod models {
|
|||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
use std::fmt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub enum Order {
|
||||||
|
Asc,
|
||||||
|
Desc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Order {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
Order::Asc => "ASC",
|
||||||
|
Order::Desc => "DESC",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ListParams {
|
||||||
|
limit: Option<i64>,
|
||||||
|
offset: Option<i64>,
|
||||||
|
order_by: Option<String>,
|
||||||
|
order: Option<Order>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListParams {
|
||||||
|
pub fn get_order_by(&self) -> String {
|
||||||
|
let default_order_by = "updated_at".to_string();
|
||||||
|
let order_by = self.order_by.as_ref().unwrap_or(&default_order_by);
|
||||||
|
let order = self.order.as_ref().unwrap_or(&Order::Desc);
|
||||||
|
format!("{} {}", order_by, order)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Shop {
|
pub struct Shop {
|
||||||
pub id: Option<i32>,
|
pub id: Option<i32>,
|
||||||
@ -282,6 +349,25 @@ mod models {
|
|||||||
debug!("INSERT INTO shops ... {:.3?}", elapsed);
|
debug!("INSERT INTO shops ... {:.3?}", elapsed);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn list(db: &PgPool, list_params: ListParams) -> Result<Vec<Self>> {
|
||||||
|
let timer = std::time::Instant::now();
|
||||||
|
let result = sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"SELECT * FROM shops
|
||||||
|
ORDER BY $1
|
||||||
|
LIMIT $2
|
||||||
|
OFFSET $3",
|
||||||
|
list_params.get_order_by(),
|
||||||
|
list_params.limit.unwrap_or(10),
|
||||||
|
list_params.offset.unwrap_or(0),
|
||||||
|
)
|
||||||
|
.fetch_all(db)
|
||||||
|
.await?;
|
||||||
|
let elapsed = timer.elapsed();
|
||||||
|
debug!("SELECT * FROM shops ... {:.3?}", elapsed);
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
Loading…
Reference in New Issue
Block a user