Add a basic sync route
This commit is contained in:
parent
5ecd691e41
commit
7f445a24e0
18
src/main.rs
18
src/main.rs
@ -1,3 +1,4 @@
|
|||||||
|
use axum::response::IntoResponse;
|
||||||
use sqlx::{Connection, PgPool};
|
use sqlx::{Connection, PgPool};
|
||||||
use toggl::TogglApi;
|
use toggl::TogglApi;
|
||||||
use worker::Worker;
|
use worker::Worker;
|
||||||
@ -22,6 +23,19 @@ enum AppError {
|
|||||||
IO(#[from] std::io::Error),
|
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 {
|
struct TableSummary {
|
||||||
client_ids: Vec<u64>,
|
client_ids: Vec<u64>,
|
||||||
workspace_ids: Vec<u64>,
|
workspace_ids: Vec<u64>,
|
||||||
@ -49,5 +63,7 @@ async fn main() {
|
|||||||
|
|
||||||
let worker = Worker { db, toggl_api };
|
let worker = Worker { db, toggl_api };
|
||||||
|
|
||||||
server::serve().await.expect("Failed to start server")
|
server::serve(
|
||||||
|
worker
|
||||||
|
).await.expect("Failed to start server")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,22 @@
|
|||||||
use axum::{
|
use axum::{http::StatusCode, routing::{get, post}, Json, Router, Extension};
|
||||||
http::StatusCode,
|
use axum::response::IntoResponse;
|
||||||
routing::{get, post},
|
use chrono::TimeDelta;
|
||||||
Json, Router,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::AppError;
|
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
|
// 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
|
// run our app with hyper, listening globally on port 3000
|
||||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use support::ReqwestRateLimiter;
|
|||||||
mod support;
|
mod support;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TogglApi {
|
pub struct TogglApi {
|
||||||
client: ClientWithMiddleware,
|
client: ClientWithMiddleware,
|
||||||
pub workspace_id: u64,
|
pub workspace_id: u64,
|
||||||
|
|||||||
@ -21,13 +21,14 @@ macro_rules! cast_slice_opts {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Worker {
|
pub struct Worker {
|
||||||
pub(crate) db: PgPool,
|
pub(crate) db: PgPool,
|
||||||
pub(crate) toggl_api: TogglApi,
|
pub(crate) toggl_api: TogglApi,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Worker {
|
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")
|
let client_ids = sqlx::query!("select id from tracking_clients")
|
||||||
.fetch_all(&self.db)
|
.fetch_all(&self.db)
|
||||||
.await?;
|
.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
|
let results = self
|
||||||
.toggl_api
|
.toggl_api
|
||||||
.search(
|
.search(
|
||||||
@ -73,7 +74,7 @@ impl Worker {
|
|||||||
self.update_database(time_entries).await
|
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) {
|
if look_back > TimeDelta::days(90) {
|
||||||
return Err(AppError::LookBackTooLarge);
|
return Err(AppError::LookBackTooLarge);
|
||||||
}
|
}
|
||||||
@ -81,7 +82,7 @@ impl Worker {
|
|||||||
self.update_time_entries(Utc::now() - look_back).await
|
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")
|
let result = sqlx::query!("select max(updated_at) as last_updated_at from time_entries")
|
||||||
.fetch_optional(&self.db)
|
.fetch_optional(&self.db)
|
||||||
.await
|
.await
|
||||||
@ -94,7 +95,7 @@ impl Worker {
|
|||||||
self.update_time_entries(fetch_since).await
|
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
|
let time_entries = self
|
||||||
.toggl_api
|
.toggl_api
|
||||||
.get_time_entries_for_user_modified_since(fetch_since)
|
.get_time_entries_for_user_modified_since(fetch_since)
|
||||||
@ -103,7 +104,7 @@ impl Worker {
|
|||||||
self.update_database(time_entries).await
|
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 existing_ids = self.get_ids().await?;
|
||||||
|
|
||||||
let fetch_workspaces = time_entries
|
let fetch_workspaces = time_entries
|
||||||
@ -143,7 +144,7 @@ impl Worker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn update_time_entries_chunk(
|
async fn update_time_entries_chunk(
|
||||||
&mut self,
|
&self,
|
||||||
time_entries: &[TimeEntry],
|
time_entries: &[TimeEntry],
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
let time_entries = Soa::from(time_entries);
|
let time_entries = Soa::from(time_entries);
|
||||||
@ -186,7 +187,7 @@ impl Worker {
|
|||||||
Ok(())
|
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
|
let workspaces = workspace_ids
|
||||||
.iter()
|
.iter()
|
||||||
.map(|id| self.toggl_api.get_workspace(*id));
|
.map(|id| self.toggl_api.get_workspace(*id));
|
||||||
@ -216,7 +217,7 @@ impl Worker {
|
|||||||
Ok(())
|
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 projects = self.toggl_api.get_projects().await?;
|
||||||
|
|
||||||
let fetch_clients = projects
|
let fetch_clients = projects
|
||||||
@ -273,7 +274,7 @@ impl Worker {
|
|||||||
Ok(())
|
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 = self.toggl_api.get_tags().await?;
|
||||||
let tags: Soa<Tag> = Soa::from(tags.as_slice());
|
let tags: Soa<Tag> = Soa::from(tags.as_slice());
|
||||||
|
|
||||||
@ -305,7 +306,7 @@ impl Worker {
|
|||||||
Ok(())
|
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 = self.toggl_api.get_clients().await?;
|
||||||
|
|
||||||
let clients: Soa<TrackingClient> = Soa::from(clients.as_slice());
|
let clients: Soa<TrackingClient> = Soa::from(clients.as_slice());
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user