Skip to content

Commit 94f18c8

Browse files
authored
add README fix up prices (#4)
* add README fix up prices * again
1 parent 91c5823 commit 94f18c8

7 files changed

Lines changed: 127 additions & 26 deletions

File tree

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,52 @@
11
# Rust Spice SDK
2+
3+
Rust SDK for Spice.ai
4+
5+
## Installation
6+
7+
Add the following to your `Cargo.toml`:
8+
9+
```toml
10+
[dependencies]
11+
spice-rs = { git = "https://github.com/spiceai/spice-rs", tag = "v1.0.0" }
12+
```
13+
14+
## Usage
15+
16+
### Arrow Query
17+
18+
**SQL Query**
19+
20+
```rust
21+
use spice_rs::new_spice_client;
22+
23+
let client = new_spice_client("API_KEY".to_string());
24+
let data = client.query("SELECT * FROM eth.recent_blocks LIMIT 10;".to_string(), Some(5*60)).await;
25+
```
26+
27+
### HTTP API
28+
#### Prices
29+
30+
Get the supported pairs:
31+
32+
```rust
33+
let supported_pairs = client.prices.get_supported_pairs().await;
34+
```
35+
36+
Get the latest price for a token pair:
37+
38+
```rust
39+
let price_data = client.prices.get_prices(&["BTC-USDC"]).await;
40+
```
41+
42+
Get historical data:
43+
44+
```rust
45+
let now = Utc::now();
46+
let start = now.sub(Duration::seconds(3600));
47+
48+
let historical_price_data = client.prices.get_historical_prices(&["BTC-USDC"], Some(start),Some(now), Option::None).await;
49+
```
50+
51+
## Documentation
52+
Check out our [Documentation](https://docs.spice.ai/sdks/rust) to learn more about how to use the Rust SDK.

src/client.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@ impl SpiceClient {
3535
prices: PricesClient::new(Some(http_addr), api_key),
3636
}
3737
}
38-
pub async fn query(
39-
&mut self,
40-
query: String,
41-
timeout: Option<u32>,
42-
) -> Result<FlightRecordBatchStream, Box<dyn Error>> {
43-
self.flight.query(query, timeout).await
38+
pub async fn query(&mut self, query: String) -> Result<FlightRecordBatchStream, Box<dyn Error>> {
39+
self.flight.query(query).await
4440
}
4541
}

src/flight.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use arrow_flight::decode::FlightRecordBatchStream;
22
use arrow_flight::sql::client::FlightSqlServiceClient;
3+
use core::panic;
34
use std::error::Error;
45
use tonic::transport::Channel;
56

@@ -28,13 +29,13 @@ impl SqlFlightClient {
2829

2930
pub async fn query(
3031
&mut self,
31-
query: String,
32-
_timeout: Option<u32>,
32+
query: String
3333
) -> std::result::Result<FlightRecordBatchStream, Box<dyn Error>> {
3434
match self.authenticate().await {
3535
Err(e) => return Err(e.into()),
3636
Ok(()) => {}
3737
};
38+
3839
match self.client.execute(query, Option::None).await {
3940
Ok(resp) => {
4041
for ep in resp.endpoint {

src/prices.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ use serde_derive::Deserialize;
1010
pub struct HistoricalPriceData {
1111
pub timestamp: DateTime<Utc>,
1212
pub price: f64,
13-
pub high: f64,
14-
pub low: f64,
15-
pub open: f64,
16-
pub close: f64,
13+
pub high: Option<f64>,
14+
pub low: Option<f64>,
15+
pub open: Option<f64>,
16+
pub close: Option<f64>,
1717
}
18+
1819
impl fmt::Display for HistoricalPriceData {
1920
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2021
write!(
2122
f,
22-
"Timestamp: {}, Price: {}, High: {}, Low: {}, Open: {}, Close: {}",
23+
"Timestamp: {}, Price: {}, High: {:?}, Low: {:?}, Open: {:?}, Close: {:?}",
2324
self.timestamp, self.price, self.high, self.low, self.open, self.close
2425
)
2526
}
@@ -139,8 +140,8 @@ impl PricesClient {
139140
Ok(response)
140141
}
141142

142-
pub async fn get_prices(&self, pair: &str) -> Result<LatestPricesResponse, Box<dyn Error>> {
143-
let mut url = format!("{}/v1/prices?pairs={}", self.base_url, pair);
143+
pub async fn get_prices(&self, pairs: &[&str]) -> Result<LatestPricesResponse, Box<dyn Error>> {
144+
let url = format!("{}/v1/prices?pairs={}", self.base_url, pairs.join(","));
144145

145146
let resp = self.add_headers(self.client.get(&url)).send().await?;
146147
match resp.status() {
@@ -170,23 +171,26 @@ impl PricesClient {
170171
pub async fn get_historical_prices(
171172
&self,
172173
pairs: &[&str],
173-
start: Option<i64>,
174-
end: Option<i64>,
174+
start: Option<DateTime<Utc>>,
175+
end: Option<DateTime<Utc>>,
175176
granularity: Option<&str>,
176177
) -> Result<HashMap<String, Vec<HistoricalPriceData>>, Box<dyn Error>> {
177178
let mut url = format!(
178179
"{}/v1/prices/historical?pairs={}",
179180
self.base_url,
180181
pairs.join(",")
181182
);
182-
183+
183184
if let Some(start_time) = start {
184-
url.push_str(&format!("&start={}", start_time));
185+
let timestamp = start_time.timestamp();
186+
url.push_str(&format!("&start={}", timestamp));
185187
}
186-
188+
187189
if let Some(end_time) = end {
188-
url.push_str(&format!("&end={}", end_time));
190+
let timestamp = end_time.timestamp();
191+
url.push_str(&format!("&end={}", timestamp));
189192
}
193+
190194

191195
if let Some(gran) = granularity {
192196
url.push_str(&format!("&granularity={}", gran));

tests/client_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ mod tests {
2222
let mut spice_client = new_client().await;
2323
match spice_client.query(
2424
"SELECT number, \"timestamp\", base_fee_per_gas, base_fee_per_gas / 1e9 AS base_fee_per_gas_gwei FROM eth.recent_blocks limit 10".to_string(),
25-
None).await {
25+
).await {
2626
Ok(mut flight_data_stream) => {
2727
// Read back RecordBatches
2828
while let Some(batch) = flight_data_stream.next().await {
@@ -48,7 +48,7 @@ mod tests {
4848
let mut spice_client = new_client().await;
4949
match spice_client.query(
5050
"SELECT number, \"timestamp\", base_fee_per_gas, base_fee_per_gas / 1e9 AS base_fee_per_gas_gwei FROM eth.blocks limit 2000".to_string(),
51-
None).await {
51+
).await {
5252
Ok(mut flight_data_stream) => {
5353
// Read back RecordBatches
5454
let mut num_batches = 0;

tests/price_test.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod tests {
33
use spice_rs::*;
44
use std::env;
55
use std::path::Path;
6+
use chrono::{DateTime, Utc, TimeZone};
67

78
async fn new_client() -> Client {
89
dotenv::from_path(Path::new(".env.local")).ok();
@@ -15,7 +16,7 @@ mod tests {
1516
async fn test_get_prices() {
1617
let spice_client = new_client().await;
1718
let pair = "BTC-USD";
18-
let result = spice_client.prices.get_prices(pair).await;
19+
let result = spice_client.prices.get_prices(&[pair]).await;
1920
assert!(result.is_ok());
2021
}
2122

@@ -25,12 +26,15 @@ mod tests {
2526
let pair1 = "BTC-USD";
2627
let pair2 = "ETH-USD";
2728

29+
let start_time = Utc.timestamp_opt(1697669766, 0).single();
30+
let end_time = Utc.timestamp_opt(1697756166, 0).single();
31+
2832
let result = spice_client
2933
.prices
3034
.get_historical_prices(
3135
&[pair1, pair2],
32-
Some(1697669766),
33-
Some(1697756166),
36+
start_time,
37+
end_time,
3438
Some("1h"),
3539
)
3640
.await;

tests/readme_test.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use spice_rs::new_spice_client;
4+
use std::env;
5+
use std::ops::{Add, Sub};
6+
use std::path::Path;
7+
use chrono::{Duration, Utc};
8+
9+
#[tokio::test]
10+
async fn test_readme() {
11+
dotenv::from_path(Path::new(".env.local")).ok();
12+
let api_key = env::var("API_KEY").expect("API_KEY not found");
13+
14+
match new_spice_client(api_key).await{
15+
Ok(mut client) => {
16+
let data = client.query("SELECT * FROM eth.recent_blocks LIMIT 10;".to_string()).await;
17+
if data.is_err() {
18+
assert!(false, "failed to query: {:#?}", data.expect_err(""))
19+
}
20+
let supported_pairs = client.prices.get_supported_pairs().await;
21+
if supported_pairs.is_err() {
22+
assert!(false, "failed to get supported pairs: {:#?}", supported_pairs.expect_err(""))
23+
}
24+
let price_data = client.prices.get_prices(&["BTC-USDC"]).await;
25+
if price_data.is_err() {
26+
assert!(false, "failed to get prices: {:#?}", price_data.expect_err(""))
27+
}
28+
let historical_price_data = client.prices.get_historical_prices(&["BTC-USDC"], Option::None, Option::None, Option::None).await;
29+
if historical_price_data.is_err() {
30+
assert!(false, "failed to get prices: {:#?}", historical_price_data.expect_err(""))
31+
}
32+
let now = Utc::now();
33+
let start = now.sub(Duration::seconds(3600));
34+
35+
let historical_price_data = client.prices.get_historical_prices(&["BTC-USDC"], Some(start),Some(now), Option::None).await;
36+
if historical_price_data.is_err() {
37+
assert!(false, "failed to get prices: {:#?}", historical_price_data.expect_err(""))
38+
}
39+
}
40+
Err(e) => {
41+
assert!(false, "Error: {:#?}", e);
42+
}
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)