use crate::data::live::AssetMPSCMessage; use crate::database; use crate::database::assets::update_asset_trading; use crate::pool::alpaca::AlpacaPool; use crate::pool::postgres::PostgresPool; use crate::types::{Asset, Class, Exchange}; use apca::api::v2::asset::{self, Symbol}; use apca::RequestError; use axum::{extract::Path, http::StatusCode, Extension, Json}; use log::info; use serde::Deserialize; use sqlx::types::time::OffsetDateTime; use std::sync::Arc; use tokio::sync::mpsc::Sender; pub async fn get_assets( Extension(postgres_pool): Extension, ) -> Result<(StatusCode, Json>), StatusCode> { let assets = database::assets::get_assets(&postgres_pool) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok((StatusCode::OK, Json(assets))) } pub async fn get_asset( Extension(postgres_pool): Extension, Path(symbol): Path, ) -> Result<(StatusCode, Json), StatusCode> { let asset = database::assets::get_asset(&postgres_pool, &symbol) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; match asset { Some(asset) => Ok((StatusCode::OK, Json(asset))), None => Err(StatusCode::NOT_FOUND), } } #[allow(dead_code)] #[derive(Deserialize)] pub struct AddAssetRequest { symbol: String, trading: Option, } pub async fn add_asset( Extension(postgres_pool): Extension, Extension(alpaca_pool): Extension, Extension(stock_live_mpsc_sender): Extension>>, Extension(crypto_live_mpsc_sender): Extension>>, Json(request): Json, ) -> Result<(StatusCode, Json), StatusCode> { if database::assets::get_asset(&postgres_pool, &request.symbol) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .is_some() { return Err(StatusCode::CONFLICT); } let asset = alpaca_pool .get() .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .issue::(&Symbol::Sym(request.symbol)) .await .map_err(|e| match e { RequestError::Endpoint(_) => StatusCode::NOT_FOUND, _ => StatusCode::INTERNAL_SERVER_ERROR, })?; let asset = Asset { symbol: asset.symbol, class: Class::from(asset.class) as Class, exchange: Exchange::from(asset.exchange) as Exchange, trading: request.trading.unwrap_or(false), date_added: OffsetDateTime::now_utc(), }; let asset = database::assets::add_asset(&postgres_pool, asset) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; match asset.class { Class(asset::Class::UsEquity) => { stock_live_mpsc_sender .send(AssetMPSCMessage::Added(asset.clone())) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; } Class(asset::Class::Crypto) => { crypto_live_mpsc_sender .send(AssetMPSCMessage::Added(asset.clone())) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; } _ => {} } info!("Added asset {}.", asset.symbol); Ok((StatusCode::CREATED, Json(asset))) } #[allow(dead_code)] #[derive(Deserialize)] pub struct UpdateAssetRequest { trading: bool, } pub async fn update_asset( Extension(postgres_pool): Extension, Path(symbol): Path, Json(request): Json, ) -> Result<(StatusCode, Json), StatusCode> { let asset = update_asset_trading(&postgres_pool, &symbol, request.trading) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; match asset { Some(asset) => { info!("Updated asset {}.", symbol); Ok((StatusCode::OK, Json(asset))) } None => Err(StatusCode::NOT_FOUND), } } pub async fn delete_asset( Extension(postgres_pool): Extension, Extension(stock_live_mpsc_sender): Extension>>, Extension(crypto_live_mpsc_sender): Extension>>, Path(symbol): Path, ) -> Result { let asset = database::assets::delete_asset(&postgres_pool, &symbol) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; match asset { Some(asset) => { match asset.class { Class(asset::Class::UsEquity) => { stock_live_mpsc_sender .send(AssetMPSCMessage::Removed(asset.clone())) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; } Class(asset::Class::Crypto) => { crypto_live_mpsc_sender .send(AssetMPSCMessage::Removed(asset.clone())) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; } _ => {} } info!("Deleted asset {}.", symbol); Ok(StatusCode::NO_CONTENT) } None => Err(StatusCode::NOT_FOUND), } }