use crate::error::Result;
use crate::response::ApiResponse;
use mbinary::enums::Dataset;
use mbinary::symbols::Instrument;
use mbinary::vendors::Vendors;
use reqwest::{self, Client, ClientBuilder};
use reqwest::{Response, StatusCode};
use std::time::Duration;
#[derive(Clone)]
pub struct Instruments {
base_url: String,
client: Client,
}
impl Instruments {
pub fn new(base_url: &str) -> Self {
let client = ClientBuilder::new()
.timeout(Duration::from_secs(20000)) .build()
.expect("Failed to build HTTP client");
Instruments {
base_url: base_url.to_string(),
client,
}
}
fn url(&self, endpoint: &str) -> String {
format!(
"{}{}{}",
self.base_url,
"/instruments/".to_string(),
endpoint.to_string()
)
}
pub async fn create_symbol(&self, instrument: &Instrument) -> Result<ApiResponse<u32>> {
let url = self.url("create");
let response: Response = self.client.post(&url).json(instrument).send().await?;
if response.status() != StatusCode::OK {
return ApiResponse::<u32>::from_response(response).await;
}
let api_response = ApiResponse::<u32>::from_response(response).await?;
Ok(api_response)
}
pub async fn get_symbol(
&self,
ticker: &String,
dataset: &Dataset,
) -> Result<ApiResponse<Vec<Instrument>>> {
let url = self.url("get");
let response = self
.client
.get(&url)
.json(&(ticker, dataset))
.send()
.await?;
if response.status() != StatusCode::OK {
return ApiResponse::<Vec<Instrument>>::from_response(response).await;
}
let api_response = ApiResponse::<Vec<Instrument>>::from_response(response).await?;
Ok(api_response)
}
pub async fn delete_symbol(&self, id: &i32) -> Result<ApiResponse<String>> {
let url = self.url("delete");
let response = self.client.delete(&url).json(id).send().await?;
if response.status() != StatusCode::OK {
return ApiResponse::<String>::from_response(response).await;
}
let api_response = ApiResponse::<String>::from_response(response).await?;
Ok(api_response)
}
pub async fn update_symbol(
&self,
instrument: &Instrument,
) -> Result<ApiResponse<String>> {
let url = self.url("update");
let response = self.client.put(&url).json(&instrument).send().await?;
if response.status() != StatusCode::OK {
return ApiResponse::<String>::from_response(response).await;
}
let api_response = ApiResponse::<String>::from_response(response).await?;
Ok(api_response)
}
pub async fn list_dataset_symbols(
&self,
dataset: &Dataset,
) -> Result<ApiResponse<Vec<Instrument>>> {
let url = self.url("list_dataset");
let response = self.client.get(&url).json(dataset).send().await?;
if response.status() != StatusCode::OK {
return ApiResponse::<Vec<Instrument>>::from_response(response).await;
}
let api_response = ApiResponse::<Vec<Instrument>>::from_response(response).await?;
Ok(api_response)
}
pub async fn list_vendor_symbols(
&self,
vendor: &Vendors,
dataset: &Dataset,
) -> Result<ApiResponse<Vec<Instrument>>> {
let url = self.url("list_vendor");
let response = self
.client
.get(&url)
.json(&(vendor, dataset))
.send()
.await?;
if response.status() != StatusCode::OK {
return ApiResponse::<Vec<Instrument>>::from_response(response).await;
}
let api_response = ApiResponse::<Vec<Instrument>>::from_response(response).await?;
Ok(api_response)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
use dotenv::dotenv;
use mbinary::enums::Dataset;
use mbinary::symbols::Instrument;
use mbinary::vendors::Vendors;
use mbinary::vendors::{DatabentoData, VendorData};
use serial_test::serial;
async fn create_dummy_instrument(client: &Instruments) -> anyhow::Result<i32> {
let schema = dbn::Schema::from_str("mbp-1")?;
let dbn_dataset = dbn::Dataset::from_str("GLBX.MDP3")?;
let stype = dbn::SType::from_str("raw_symbol")?;
let vendor_data = VendorData::Databento(DatabentoData {
schema,
dataset: dbn_dataset,
stype,
});
let instrument = Instrument::new(
None,
"AAPL",
"Apple tester client",
Dataset::Equities,
Vendors::Databento,
vendor_data.encode(),
1,
1,
1,
false,
true,
);
let create_response = client.create_symbol(&instrument).await?;
let id = create_response.data as i32;
Ok(id)
}
#[tokio::test]
#[serial]
async fn test_instrument_create() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let schema = dbn::Schema::from_str("mbp-1")?;
let dbn_dataset = dbn::Dataset::from_str("GLBX.MDP3")?;
let stype = dbn::SType::from_str("raw_symbol")?;
let vendor_data = VendorData::Databento(DatabentoData {
schema,
dataset: dbn_dataset,
stype,
});
let instrument = Instrument::new(
None,
"AAP00001",
"Apple tester client",
Dataset::Equities,
Vendors::Databento,
vendor_data.encode(),
1,
1,
1,
false,
true,
);
let response = client.create_symbol(&instrument).await?;
let id = response.data as i32;
assert_eq!(response.code, 200);
assert_eq!(response.status, "success");
let _ = client.delete_symbol(&id).await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_instrument_create_error() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let id = create_dummy_instrument(&client).await?;
let schema = dbn::Schema::from_str("mbp-1")?;
let dbn_dataset = dbn::Dataset::from_str("GLBX.MDP3")?;
let stype = dbn::SType::from_str("raw_symbol")?;
let vendor_data = VendorData::Databento(DatabentoData {
schema,
dataset: dbn_dataset,
stype,
});
let instrument = Instrument::new(
None,
"AAPL",
"Apple tester client",
Dataset::Equities,
Vendors::Databento,
vendor_data.encode(),
1,
1,
1,
false,
true,
);
let response = client.create_symbol(&instrument).await?;
assert_eq!(response.code, 500);
assert_eq!(response.status, "failed");
let _ = client.delete_symbol(&id).await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_get_instrument() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let id = create_dummy_instrument(&client).await?;
let response = client
.get_symbol(&"AAPL".to_string(), &Dataset::Equities)
.await?;
println!("{:?}", response);
assert_eq!(response.code, 200);
assert_eq!(response.status, "success");
assert!(response.data.len() > 0);
let _ = client.delete_symbol(&id).await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_get_instrument_none() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let response = client
.get_symbol(&("AAPL9".to_string()), &Dataset::Equities)
.await?;
assert_eq!(response.code, 404); assert_eq!(response.status, "success");
Ok(())
}
#[tokio::test]
#[serial]
async fn test_update_instrument() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let id = create_dummy_instrument(&client).await?;
let schema = dbn::Schema::from_str("mbp-1")?;
let dbn_dataset = dbn::Dataset::from_str("GLBX.MDP3")?;
let stype = dbn::SType::from_str("raw_symbol")?;
let vendor_data = VendorData::Databento(DatabentoData {
schema,
dataset: dbn_dataset,
stype,
});
let instrument = Instrument::new(
Some(id as u32),
"TTT0005",
"New name",
Dataset::Equities,
Vendors::Databento,
vendor_data.encode(),
1,
2,
1,
false,
true,
);
let response = client.update_symbol(&instrument).await?;
assert_eq!(response.code, 200);
assert_eq!(response.status, "success");
let _ = client.delete_symbol(&id).await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_list_dataset_instruments() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let id = create_dummy_instrument(&client).await?;
let dataset = Dataset::Equities;
let response = client.list_dataset_symbols(&dataset).await?;
assert_eq!(response.code, 200);
assert_eq!(response.status, "success");
let _ = client.delete_symbol(&id).await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_list_vendor_instruments() -> anyhow::Result<()> {
dotenv().ok();
let base_url = std::env::var("MIDAS_URL").expect("Expected database_url.");
let client = Instruments::new(&base_url);
let id = create_dummy_instrument(&client).await?;
let dataset = Dataset::Equities;
let vendor = Vendors::Databento;
let response = client.list_vendor_symbols(&vendor, &dataset).await?;
assert_eq!(response.code, 200);
assert_eq!(response.status, "success");
let _ = client.delete_symbol(&id).await?;
Ok(())
}
}