Use proper query parameters
This commit is contained in:
@@ -29,7 +29,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let d = CreateDatapoint::new(1.0)
|
let d = CreateDatapoint::new(1.0)
|
||||||
.with_comment("Test datapoint")
|
.with_comment("Test #hashtag datapoint")
|
||||||
.with_requestid("unique-id-42");
|
.with_requestid("unique-id-42");
|
||||||
match client.create_datapoint("me", "meditation", &d).await {
|
match client.create_datapoint("me", "meditation", &d).await {
|
||||||
Ok(datapoint) => println!("Added: {datapoint:#?}"),
|
Ok(datapoint) => println!("Added: {datapoint:#?}"),
|
||||||
|
|||||||
109
src/lib.rs
109
src/lib.rs
@@ -1,5 +1,5 @@
|
|||||||
pub mod types;
|
pub mod types;
|
||||||
use crate::types::{CreateDatapoint, Datapoint, UserInfo, UserInfoDiff};
|
use crate::types::{CreateDatapoint, Datapoint, Goal, UserInfo, UserInfoDiff};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@@ -16,29 +16,42 @@ pub struct BeeminderClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BeeminderClient {
|
impl BeeminderClient {
|
||||||
async fn request<T>(&self, endpoint: &str) -> Result<T, Error>
|
async fn request<T>(
|
||||||
|
&self,
|
||||||
|
endpoint: &str,
|
||||||
|
params: Option<Vec<(&str, &str)>>,
|
||||||
|
) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
|
let mut query = vec![("auth_token", self.api_key.as_str())];
|
||||||
|
if let Some(additional_params) = params {
|
||||||
|
query.extend(additional_params);
|
||||||
|
}
|
||||||
|
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
.get(format!("{}{}", self.base_url, endpoint))
|
.get(format!("{}{}", self.base_url, endpoint))
|
||||||
.query(&[("auth_token", &self.api_key)])
|
.query(&query)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status()?;
|
||||||
|
|
||||||
response.json().await.map_err(Error::from)
|
response.json().await.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post<T>(&self, endpoint: &str) -> Result<T, Error>
|
async fn post<T>(&self, endpoint: &str, params: Option<Vec<(&str, &str)>>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
|
let mut query = vec![("auth_token", self.api_key.as_str())];
|
||||||
|
if let Some(additional_params) = params {
|
||||||
|
query.extend(additional_params);
|
||||||
|
}
|
||||||
|
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
.post(format!("{}{}", self.base_url, endpoint))
|
.post(format!("{}{}", self.base_url, endpoint))
|
||||||
.query(&[("auth_token", &self.api_key)])
|
.query(&query)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status()?;
|
||||||
@@ -61,7 +74,7 @@ impl BeeminderClient {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns an error if the HTTP request fails or response cannot be parsed.
|
/// Returns an error if the HTTP request fails or response cannot be parsed.
|
||||||
pub async fn get_user(&self, username: &str) -> Result<UserInfo, Error> {
|
pub async fn get_user(&self, username: &str) -> Result<UserInfo, Error> {
|
||||||
self.request(&format!("users/{username}.json")).await
|
self.request(&format!("users/{username}.json"), None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves detailed user information with changes since the specified timestamp.
|
/// Retrieves detailed user information with changes since the specified timestamp.
|
||||||
@@ -73,8 +86,9 @@ impl BeeminderClient {
|
|||||||
username: &str,
|
username: &str,
|
||||||
diff_since: OffsetDateTime,
|
diff_since: OffsetDateTime,
|
||||||
) -> Result<UserInfoDiff, Error> {
|
) -> Result<UserInfoDiff, Error> {
|
||||||
let timestamp = diff_since.unix_timestamp();
|
let diff_since = diff_since.unix_timestamp().to_string();
|
||||||
self.request(&format!("users/{username}.json?diff_since={timestamp}"))
|
let params = vec![("diff_since", diff_since.as_str())];
|
||||||
|
self.request(&format!("users/{username}.json"), Some(params))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,25 +103,17 @@ impl BeeminderClient {
|
|||||||
sort: Option<&str>,
|
sort: Option<&str>,
|
||||||
count: Option<u64>,
|
count: Option<u64>,
|
||||||
) -> Result<Vec<Datapoint>, Error> {
|
) -> Result<Vec<Datapoint>, Error> {
|
||||||
let mut endpoint = format!("users/{username}/goals/{goal}/datapoints.json");
|
let mut params = Vec::new();
|
||||||
|
params.push(("sort", sort.unwrap_or("timestamp")));
|
||||||
let mut query = Vec::new();
|
|
||||||
|
|
||||||
if let Some(sort) = sort {
|
|
||||||
query.push(format!("sort={sort}"));
|
|
||||||
} else {
|
|
||||||
query.push("sort=timestamp".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let count_str;
|
||||||
if let Some(count) = count {
|
if let Some(count) = count {
|
||||||
query.push(format!("count={count}"));
|
count_str = count.to_string();
|
||||||
|
params.push(("count", &count_str));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !query.is_empty() {
|
let endpoint = format!("users/{username}/goals/{goal}/datapoints.json");
|
||||||
endpoint = format!("{}?{}", endpoint, query.join("&"));
|
self.request(&endpoint, Some(params)).await
|
||||||
}
|
|
||||||
|
|
||||||
self.request(&endpoint).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new datapoint for a goal.
|
/// Creates a new datapoint for a goal.
|
||||||
@@ -120,23 +126,48 @@ impl BeeminderClient {
|
|||||||
goal: &str,
|
goal: &str,
|
||||||
datapoint: &CreateDatapoint,
|
datapoint: &CreateDatapoint,
|
||||||
) -> Result<Datapoint, Error> {
|
) -> Result<Datapoint, Error> {
|
||||||
let mut query = Vec::new();
|
let mut params = Vec::new();
|
||||||
query.push(format!("value={}", datapoint.value));
|
|
||||||
|
let value_str = datapoint.value.to_string();
|
||||||
|
params.push(("value", value_str.as_str()));
|
||||||
|
|
||||||
|
let timestamp_str;
|
||||||
if let Some(ts) = datapoint.timestamp {
|
if let Some(ts) = datapoint.timestamp {
|
||||||
query.push(format!("timestamp={}", ts.unix_timestamp()));
|
timestamp_str = ts.unix_timestamp().to_string();
|
||||||
}
|
params.push(("timestamp", timestamp_str.as_str()));
|
||||||
if let Some(ds) = &datapoint.daystamp {
|
|
||||||
query.push(format!("daystamp={ds}"));
|
|
||||||
}
|
|
||||||
if let Some(c) = &datapoint.comment {
|
|
||||||
query.push(format!("comment={c}"));
|
|
||||||
}
|
|
||||||
if let Some(rid) = &datapoint.requestid {
|
|
||||||
query.push(format!("requestid={rid}"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut endpoint = format!("users/{username}/goals/{goal}/datapoints.json");
|
if let Some(ds) = &datapoint.daystamp {
|
||||||
endpoint = format!("{}?{}", endpoint, query.join("&"));
|
params.push(("daystamp", ds.as_str()));
|
||||||
self.post(&endpoint).await
|
}
|
||||||
|
|
||||||
|
if let Some(c) = &datapoint.comment {
|
||||||
|
params.push(("comment", c.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rid) = &datapoint.requestid {
|
||||||
|
params.push(("requestid", rid.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let endpoint = format!("users/{username}/goals/{goal}/datapoints.json");
|
||||||
|
self.post(&endpoint, Some(params)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves all goals for a user.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error if the HTTP request fails or response cannot be parsed.
|
||||||
|
pub async fn get_goals(&self, username: &str) -> Result<Vec<Goal>, Error> {
|
||||||
|
self.request(&format!("users/{username}/goals.json"), None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves archived goals for a user.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error if the HTTP request fails or response cannot be parsed.
|
||||||
|
pub async fn get_archived_goals(&self, username: &str) -> Result<Vec<Goal>, Error> {
|
||||||
|
self.request(&format!("users/{username}/goals/archived.json"), None)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user