From 7f445a24e02e10dd4fff4adcb3906e66cca2dc4f Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Sat, 27 Jul 2024 21:18:36 +0100 Subject: [PATCH] Add a basic sync route --- src/main.rs | 18 +++++++++++++++++- src/server.rs | 22 +++++++++++++++------- src/toggl/mod.rs | 2 +- src/worker.rs | 23 ++++++++++++----------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index ac3c4e0..5dfb8b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use axum::response::IntoResponse; use sqlx::{Connection, PgPool}; use toggl::TogglApi; use worker::Worker; @@ -22,6 +23,19 @@ enum AppError { IO(#[from] std::io::Error), } +impl IntoResponse for AppError { + fn into_response(self) -> axum::http::Response { + let status = match &self { + AppError::SqlxError(_) => axum::http::StatusCode::INTERNAL_SERVER_ERROR, + AppError::TogglError(_) => axum::http::StatusCode::INTERNAL_SERVER_ERROR, + AppError::LookBackTooLarge => axum::http::StatusCode::BAD_REQUEST, + AppError::IO(_) => axum::http::StatusCode::INTERNAL_SERVER_ERROR, + }; + + (status, self.to_string()).into_response() + } +} + struct TableSummary { client_ids: Vec, workspace_ids: Vec, @@ -49,5 +63,7 @@ async fn main() { let worker = Worker { db, toggl_api }; - server::serve().await.expect("Failed to start server") + server::serve( + worker + ).await.expect("Failed to start server") } diff --git a/src/server.rs b/src/server.rs index 4914479..17b2b7c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,14 +1,22 @@ -use axum::{ - http::StatusCode, - routing::{get, post}, - Json, Router, -}; +use axum::{http::StatusCode, routing::{get, post}, Json, Router, Extension}; +use axum::response::IntoResponse; +use chrono::TimeDelta; use crate::AppError; +use crate::worker::Worker; -pub async fn serve() -> Result<(), AppError> { +async fn sync(Extension(worker): Extension) -> Result { + worker.update(TimeDelta::days(30)).await?; + + Ok("Ok") +} + +pub async fn serve(worker: Worker) -> Result<(), AppError> { // build our application with a route - let app = Router::new(); + let app = Router::new() + .route("/health", get(|| async { "Ok" })) + .route("/sync", post(sync)) + .layer(Extension(worker)); // run our app with hyper, listening globally on port 3000 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; diff --git a/src/toggl/mod.rs b/src/toggl/mod.rs index 0665b03..f18ef9e 100644 --- a/src/toggl/mod.rs +++ b/src/toggl/mod.rs @@ -13,7 +13,7 @@ use support::ReqwestRateLimiter; mod support; pub mod types; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct TogglApi { client: ClientWithMiddleware, pub workspace_id: u64, diff --git a/src/worker.rs b/src/worker.rs index be91894..dd5fcc6 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -21,13 +21,14 @@ macro_rules! cast_slice_opts { }; } +#[derive(Debug, Clone)] pub struct Worker { pub(crate) db: PgPool, pub(crate) toggl_api: TogglApi, } impl Worker { - async fn get_ids(&mut self) -> Result { + async fn get_ids(&self) -> Result { let client_ids = sqlx::query!("select id from tracking_clients") .fetch_all(&self.db) .await?; @@ -52,7 +53,7 @@ impl Worker { }) } - pub async fn fetch_within(&mut self, start: NaiveDate, end: NaiveDate) -> Result<(), AppError> { + pub async fn fetch_within(&self, start: NaiveDate, end: NaiveDate) -> Result<(), AppError> { let results = self .toggl_api .search( @@ -73,7 +74,7 @@ impl Worker { self.update_database(time_entries).await } - pub async fn fetch_changed_since(&mut self, look_back: TimeDelta) -> Result<(), AppError> { + pub async fn fetch_changed_since(&self, look_back: TimeDelta) -> Result<(), AppError> { if look_back > TimeDelta::days(90) { return Err(AppError::LookBackTooLarge); } @@ -81,7 +82,7 @@ impl Worker { self.update_time_entries(Utc::now() - look_back).await } - pub async fn update(&mut self, default_look_back: TimeDelta) -> Result<(), AppError> { + pub async fn update(&self, default_look_back: TimeDelta) -> Result<(), AppError> { let result = sqlx::query!("select max(updated_at) as last_updated_at from time_entries") .fetch_optional(&self.db) .await @@ -94,7 +95,7 @@ impl Worker { self.update_time_entries(fetch_since).await } - async fn update_time_entries(&mut self, fetch_since: DateTime) -> Result<(), AppError> { + async fn update_time_entries(&self, fetch_since: DateTime) -> Result<(), AppError> { let time_entries = self .toggl_api .get_time_entries_for_user_modified_since(fetch_since) @@ -103,7 +104,7 @@ impl Worker { self.update_database(time_entries).await } - async fn update_database(&mut self, time_entries: Vec) -> Result<(), AppError> { + async fn update_database(&self, time_entries: Vec) -> Result<(), AppError> { let existing_ids = self.get_ids().await?; let fetch_workspaces = time_entries @@ -143,7 +144,7 @@ impl Worker { } async fn update_time_entries_chunk( - &mut self, + &self, time_entries: &[TimeEntry], ) -> Result<(), AppError> { let time_entries = Soa::from(time_entries); @@ -186,7 +187,7 @@ impl Worker { Ok(()) } - async fn update_workspaces(&mut self, workspace_ids: &[u64]) -> Result<(), AppError> { + async fn update_workspaces(&self, workspace_ids: &[u64]) -> Result<(), AppError> { let workspaces = workspace_ids .iter() .map(|id| self.toggl_api.get_workspace(*id)); @@ -216,7 +217,7 @@ impl Worker { Ok(()) } - async fn update_projects(&mut self, existing_ids: &TableSummary) -> Result<(), AppError> { + async fn update_projects(&self, existing_ids: &TableSummary) -> Result<(), AppError> { let projects = self.toggl_api.get_projects().await?; let fetch_clients = projects @@ -273,7 +274,7 @@ impl Worker { Ok(()) } - pub(crate) async fn update_tags(&mut self) -> Result<(), AppError> { + pub(crate) async fn update_tags(&self) -> Result<(), AppError> { let tags = self.toggl_api.get_tags().await?; let tags: Soa = Soa::from(tags.as_slice()); @@ -305,7 +306,7 @@ impl Worker { Ok(()) } - async fn update_clients(&mut self) -> Result<(), AppError> { + async fn update_clients(&self) -> Result<(), AppError> { let clients = self.toggl_api.get_clients().await?; let clients: Soa = Soa::from(clients.as_slice());