Implement datapoint update endpoint
This commit is contained in:
@@ -12,4 +12,4 @@ reqwest = { version = "^0.12", features = ["json"] }
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
thiserror = "^2.0"
|
||||
tokio = { version = "^1.42", features = ["full"] }
|
||||
time = { version = "^0.3", features = ["serde", "parsing", "formatting", "macros"] }
|
||||
time = { version = "^0.3", features = ["serde", "parsing", "formatting"]}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use beeminder::types::CreateDatapoint;
|
||||
use beeminder::types::{CreateDatapoint, UpdateDatapoint};
|
||||
use beeminder::BeeminderClient;
|
||||
use std::env;
|
||||
use time::macros::datetime;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@@ -14,22 +14,44 @@ async fn main() {
|
||||
Err(e) => println!("{e:#?}"),
|
||||
}
|
||||
|
||||
let since = datetime!(2024-12-13 20:00 UTC);
|
||||
let since = OffsetDateTime::now_utc() - Duration::days(2);
|
||||
match client.get_user_diff(since).await {
|
||||
Ok(user) => println!("{user:#?}"),
|
||||
Err(e) => println!("{e:#?}"),
|
||||
}
|
||||
|
||||
match client.get_datapoints("meditation", None, Some(2)).await {
|
||||
Ok(datapoints) => println!("{datapoints:#?}"),
|
||||
Err(e) => println!("{e:#?}"),
|
||||
}
|
||||
|
||||
let d = CreateDatapoint::new(1.0)
|
||||
.with_comment("Test #hashtag datapoint")
|
||||
.with_requestid("unique-id-42");
|
||||
match client.create_datapoint("meditation", &d).await {
|
||||
let new_datapoint = CreateDatapoint::new(20.0)
|
||||
.with_comment("I did some pushups!")
|
||||
.with_requestid("unique-pushup-id-42");
|
||||
match client.create_datapoint("pushups", &new_datapoint).await {
|
||||
Ok(datapoint) => println!("Added: {datapoint:#?}"),
|
||||
Err(e) => println!("{e:#?}"),
|
||||
}
|
||||
|
||||
let goal_name = "pushups";
|
||||
match client.get_datapoints(&goal_name, None, Some(3)).await {
|
||||
Ok(datapoints) => {
|
||||
if let Some(first_datapoint) = datapoints.first() {
|
||||
let update_datapoint = UpdateDatapoint::from(first_datapoint)
|
||||
.with_value(40.0)
|
||||
.with_comment("Much better.");
|
||||
|
||||
match client.update_datapoint(&goal_name, &update_datapoint).await {
|
||||
Ok(datapoint) => println!("Updated: {datapoint:#?}"),
|
||||
Err(e) => println!("Update error: {e:#?}"),
|
||||
}
|
||||
|
||||
match client
|
||||
.delete_datapoint(&goal_name, &update_datapoint.id)
|
||||
.await
|
||||
{
|
||||
Ok(datapoint) => println!("Deleted: {datapoint:#?}"),
|
||||
Err(e) => println!("Delete error: {e:#?}"),
|
||||
}
|
||||
} else {
|
||||
println!("No datapoints found");
|
||||
}
|
||||
}
|
||||
Err(e) => println!("Get datapoints error: {e:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
36
src/lib.rs
36
src/lib.rs
@@ -1,5 +1,7 @@
|
||||
pub mod types;
|
||||
use crate::types::{CreateDatapoint, Datapoint, GoalSummary, UserInfo, UserInfoDiff};
|
||||
use crate::types::{
|
||||
CreateDatapoint, Datapoint, GoalSummary, UpdateDatapoint, UserInfo, UserInfoDiff,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -123,7 +125,37 @@ impl BeeminderClient {
|
||||
self.post(&endpoint, datapoint).await
|
||||
}
|
||||
|
||||
/// Deletes a specific datapoint for the user's goal.
|
||||
/// Updates an existing datapoint for a goal.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `goal` - The slug/name of the goal to update
|
||||
/// * `update` - The datapoint update containing the ID and fields to update
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if the HTTP request fails or if the response cannot be parsed
|
||||
pub async fn update_datapoint(
|
||||
&self,
|
||||
goal: &str,
|
||||
update: &UpdateDatapoint,
|
||||
) -> Result<Datapoint, Error> {
|
||||
let endpoint = format!(
|
||||
"users/{}/goals/{}/datapoints/{}.json",
|
||||
self.username, goal, update.id
|
||||
);
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.put(format!("{}{}", self.base_url, endpoint))
|
||||
.query(&[("auth_token", self.api_key.as_str())])
|
||||
.query(update)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
response.json().await.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Deletes a specific datapoint for a goal.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `goal` - The name of the goal.
|
||||
|
||||
76
src/types.rs
76
src/types.rs
@@ -171,6 +171,82 @@ impl CreateDatapoint {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters for updating an existing datapoint
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct UpdateDatapoint {
|
||||
/// ID of the datapoint to update
|
||||
#[serde(skip_serializing)]
|
||||
pub id: String,
|
||||
/// Optional new timestamp for the datapoint
|
||||
#[serde(
|
||||
with = "time::serde::timestamp::option",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub timestamp: Option<OffsetDateTime>,
|
||||
/// Optional new value
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub value: Option<f64>,
|
||||
/// Optional new comment
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
impl From<&Datapoint> for UpdateDatapoint {
|
||||
fn from(datapoint: &Datapoint) -> Self {
|
||||
Self {
|
||||
id: datapoint.id.clone(),
|
||||
timestamp: Some(datapoint.timestamp),
|
||||
value: Some(datapoint.value),
|
||||
comment: datapoint.comment.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdateDatapoint {
|
||||
/// Creates an empty update for the given datapoint ID
|
||||
#[must_use]
|
||||
pub fn new(id: impl Into<String>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
timestamp: None,
|
||||
value: None,
|
||||
comment: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an update from an existing datapoint with no changes
|
||||
#[must_use]
|
||||
pub fn from_datapoint(datapoint: &Datapoint) -> Self {
|
||||
Self {
|
||||
id: datapoint.id.clone(),
|
||||
timestamp: Some(datapoint.timestamp),
|
||||
value: Some(datapoint.value),
|
||||
comment: datapoint.comment.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a new timestamp
|
||||
#[must_use]
|
||||
pub fn with_timestamp(mut self, timestamp: OffsetDateTime) -> Self {
|
||||
self.timestamp = Some(timestamp);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a new value
|
||||
#[must_use]
|
||||
pub fn with_value(mut self, value: f64) -> Self {
|
||||
self.value = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a new comment
|
||||
#[must_use]
|
||||
pub fn with_comment(mut self, comment: &str) -> Self {
|
||||
self.comment = Some(comment.to_string());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserInfo {
|
||||
/// Username of the Beeminder account
|
||||
|
||||
Reference in New Issue
Block a user