Add a basic sync route

This commit is contained in:
Joshua Coles 2024-07-27 21:18:36 +01:00
parent 5ecd691e41
commit 7f445a24e0
4 changed files with 45 additions and 20 deletions

View File

@ -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<axum::body::Body> {
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<u64>,
workspace_ids: Vec<u64>,
@ -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")
}

View File

@ -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<Worker>) -> Result<impl IntoResponse, AppError> {
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?;

View File

@ -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,

View File

@ -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<TableSummary, AppError> {
async fn get_ids(&self) -> Result<TableSummary, AppError> {
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<Utc>) -> Result<(), AppError> {
async fn update_time_entries(&self, fetch_since: DateTime<Utc>) -> 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<TimeEntry>) -> Result<(), AppError> {
async fn update_database(&self, time_entries: Vec<TimeEntry>) -> 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<Tag> = 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<TrackingClient> = Soa::from(clients.as_slice());