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"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
thiserror = "^2.0"
|
thiserror = "^2.0"
|
||||||
tokio = { version = "^1.42", features = ["full"] }
|
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 beeminder::BeeminderClient;
|
||||||
use std::env;
|
use std::env;
|
||||||
use time::macros::datetime;
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -14,22 +14,44 @@ async fn main() {
|
|||||||
Err(e) => println!("{e:#?}"),
|
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 {
|
match client.get_user_diff(since).await {
|
||||||
Ok(user) => println!("{user:#?}"),
|
Ok(user) => println!("{user:#?}"),
|
||||||
Err(e) => println!("{e:#?}"),
|
Err(e) => println!("{e:#?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
match client.get_datapoints("meditation", None, Some(2)).await {
|
let new_datapoint = CreateDatapoint::new(20.0)
|
||||||
Ok(datapoints) => println!("{datapoints:#?}"),
|
.with_comment("I did some pushups!")
|
||||||
Err(e) => println!("{e:#?}"),
|
.with_requestid("unique-pushup-id-42");
|
||||||
}
|
match client.create_datapoint("pushups", &new_datapoint).await {
|
||||||
|
|
||||||
let d = CreateDatapoint::new(1.0)
|
|
||||||
.with_comment("Test #hashtag datapoint")
|
|
||||||
.with_requestid("unique-id-42");
|
|
||||||
match client.create_datapoint("meditation", &d).await {
|
|
||||||
Ok(datapoint) => println!("Added: {datapoint:#?}"),
|
Ok(datapoint) => println!("Added: {datapoint:#?}"),
|
||||||
Err(e) => println!("{e:#?}"),
|
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;
|
pub mod types;
|
||||||
use crate::types::{CreateDatapoint, Datapoint, GoalSummary, UserInfo, UserInfoDiff};
|
use crate::types::{
|
||||||
|
CreateDatapoint, Datapoint, GoalSummary, UpdateDatapoint, UserInfo, UserInfoDiff,
|
||||||
|
};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@@ -123,7 +125,37 @@ impl BeeminderClient {
|
|||||||
self.post(&endpoint, datapoint).await
|
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
|
/// # Arguments
|
||||||
/// * `goal` - The name of the goal.
|
/// * `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)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct UserInfo {
|
pub struct UserInfo {
|
||||||
/// Username of the Beeminder account
|
/// Username of the Beeminder account
|
||||||
|
|||||||
Reference in New Issue
Block a user