use crate::AppState;
use axum::{
Json, Router,
extract::{Path, Query, State},
http::StatusCode,
response::IntoResponse,
routing,
};
use fts_core::{
models::{
AuctionOutcome, DateTimeRangeQuery, DateTimeRangeResponse, ProductData, ProductId,
ProductQuery, ProductQueryResponse, ProductRecord,
},
ports::{MarketRepository, ProductRepository},
};
use tracing::{Level, event};
pub fn router<T: MarketRepository>() -> Router<AppState<T>> {
Router::new()
.route("/", routing::get(list_products))
.route("/{product_id}", routing::get(get_product))
.route("/{product_id}/outcomes", routing::get(product_outcomes))
}
#[utoipa::path(
get,
path = "/v0/products",
responses(
(status = OK, body = ProductQueryResponse<ProductRecord<ProductData>, ProductQuery>),
(status = INTERNAL_SERVER_ERROR)
),
params(
ProductQuery
),
tags = ["products"]
)]
async fn list_products<T: MarketRepository>(
State(state): State<AppState<T>>,
Query(query): Query<T::ProductQuery>,
) -> Result<impl IntoResponse, StatusCode> {
let results = T::query_products(&state.market, query, 100).await;
match results {
Ok(results) => Ok((StatusCode::OK, Json(results))),
Err(e) => {
event!(Level::ERROR, error = ?e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
#[utoipa::path(
get,
path = "/v0/products/{product_id}",
responses(
(status = OK, body = ProductRecord<ProductData>),
(status = NOT_FOUND),
(status = INTERNAL_SERVER_ERROR)
),
params(
("product_id" = ProductId, description = "Unique identifier of the product")
),
tags = ["products"]
)]
async fn get_product<T: MarketRepository>(
State(state): State<AppState<T>>,
Path(product_id): Path<ProductId>,
) -> Result<impl IntoResponse, StatusCode> {
let data = T::view_product(&state.market, product_id).await;
match data {
Ok(Some(data)) => Ok((StatusCode::OK, Json(data))),
Ok(None) => Err(StatusCode::NOT_FOUND),
Err(e) => {
event!(Level::ERROR, error = ?e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
type ProductOutcomeBody = DateTimeRangeResponse<AuctionOutcome>;
#[utoipa::path(
get,
path = "/v0/products/{product_id}/outcomes",
responses(
(status = OK, body = ProductOutcomeBody),
(status = INTERNAL_SERVER_ERROR)
),
params(
("product_id" = ProductId, description = "Unique identifier of the product"),
DateTimeRangeQuery
),
tags = ["products", "outcomes"]
)]
async fn product_outcomes<T: MarketRepository>(
State(state): State<AppState<T>>,
Path(product_id): Path<ProductId>,
Query(query): Query<DateTimeRangeQuery>,
) -> Result<impl IntoResponse, StatusCode> {
let data = ProductRepository::get_outcomes(&state.market, product_id, query, 48).await;
match data {
Ok(data) => Ok((StatusCode::OK, Json(data))),
Err(e) => {
event!(Level::ERROR, error = ?e);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}