Stash sync logic
This commit is contained in:
parent
5887ae4946
commit
5b4f850b8f
70
Cargo.lock
generated
70
Cargo.lock
generated
@ -1116,6 +1116,16 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint-dig"
|
name = "num-bigint-dig"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@ -1238,6 +1248,12 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -1844,6 +1860,15 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -2234,6 +2259,16 @@ dependencies = [
|
|||||||
"syn 2.0.71",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.36"
|
version = "0.3.36"
|
||||||
@ -2288,6 +2323,7 @@ dependencies = [
|
|||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"futures",
|
||||||
"governor",
|
"governor",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
@ -2300,6 +2336,8 @@ dependencies = [
|
|||||||
"sqlx",
|
"sqlx",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2436,6 +2474,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"nu-ansi-term",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2512,6 +2576,12 @@ version = "2.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|||||||
@ -21,3 +21,6 @@ url = "2.5.2"
|
|||||||
serde_with = "3.9.0"
|
serde_with = "3.9.0"
|
||||||
sqlx = { version = "0.7.4", features = ["postgres", "runtime-tokio", "macros", "chrono"] }
|
sqlx = { version = "0.7.4", features = ["postgres", "runtime-tokio", "macros", "chrono"] }
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
futures = "0.3.30"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = "0.3.18"
|
||||||
|
|||||||
132
src/main.rs
132
src/main.rs
@ -1,4 +1,4 @@
|
|||||||
use chrono::{NaiveDate, TimeDelta, Utc};
|
use chrono::{TimeDelta, Utc};
|
||||||
use sqlx::{Connection, PgConnection};
|
use sqlx::{Connection, PgConnection};
|
||||||
use toggl::TogglApi;
|
use toggl::TogglApi;
|
||||||
|
|
||||||
@ -65,21 +65,75 @@ impl Worker {
|
|||||||
let time_entries = self.toggl_api
|
let time_entries = self.toggl_api
|
||||||
.get_time_entries_for_user_modified_since(fetch_since).await?;
|
.get_time_entries_for_user_modified_since(fetch_since).await?;
|
||||||
|
|
||||||
let refetch_projects = time_entries.iter()
|
let fetch_workspaces = time_entries.iter()
|
||||||
|
.map(|entry| entry.workspace_id)
|
||||||
|
.filter(|workspace_id| !existing_ids.workspace_ids.contains(&workspace_id))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let fetch_projects = time_entries.iter()
|
||||||
.map(|entry| entry.project_id)
|
.map(|entry| entry.project_id)
|
||||||
.filter_map(|project_id| project_id)
|
.filter_map(|project_id| project_id)
|
||||||
.any(|project_id| !existing_ids.project_ids.contains(&project_id));
|
.any(|project_id| !existing_ids.project_ids.contains(&project_id));
|
||||||
|
|
||||||
let refetch_tags = time_entries.iter()
|
let fetch_tags = time_entries.iter()
|
||||||
.flat_map(|entry| entry.tag_ids.iter())
|
.flat_map(|entry| entry.tag_ids.iter())
|
||||||
.any(|tag| !existing_ids.tag_ids.contains(&tag));
|
.any(|tag| !existing_ids.tag_ids.contains(&tag));
|
||||||
|
|
||||||
|
if !fetch_workspaces.is_empty() {
|
||||||
|
self.update_workspaces(&fetch_workspaces).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if fetch_projects {
|
||||||
|
self.update_projects(&existing_ids).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if fetch_tags {
|
||||||
|
self.update_tags().await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_projects(&mut self) -> Result<(), AppError> {
|
async fn update_workspaces(&mut self, workspace_ids: &[u64]) -> Result<(), AppError> {
|
||||||
|
let workspaces = workspace_ids.iter()
|
||||||
|
.map(|id| self.toggl_api.get_workspace(*id));
|
||||||
|
|
||||||
|
let workspaces = futures::future::join_all(workspaces).await
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
for workspace in workspaces {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO workspaces (id, organization_id, name)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
|
organization_id = excluded.organization_id,
|
||||||
|
name = excluded.name
|
||||||
|
"#,
|
||||||
|
workspace.id as i64,
|
||||||
|
workspace.organization_id as i64,
|
||||||
|
workspace.name,
|
||||||
|
)
|
||||||
|
.execute(&mut self.db)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_projects(&mut 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.iter()
|
||||||
|
.map(|project| project.client_id)
|
||||||
|
.filter_map(|client_id| client_id)
|
||||||
|
.any(|client_id| !existing_ids.client_ids.contains(&(client_id as u64)));
|
||||||
|
|
||||||
|
if fetch_clients {
|
||||||
|
self.update_clients().await?;
|
||||||
|
}
|
||||||
|
|
||||||
for project in projects {
|
for project in projects {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
@ -123,6 +177,74 @@ impl Worker {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_tags(&mut self) -> Result<(), AppError> {
|
||||||
|
let tags = self.toggl_api.get_tags().await?;
|
||||||
|
|
||||||
|
for tag in tags {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO tags (id, name, workspace_id, creator_id, updated_at, deleted_at, permissions)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
|
name = excluded.name,
|
||||||
|
workspace_id = excluded.workspace_id,
|
||||||
|
creator_id = excluded.creator_id,
|
||||||
|
updated_at = excluded.updated_at,
|
||||||
|
deleted_at = excluded.deleted_at,
|
||||||
|
permissions = excluded.permissions
|
||||||
|
"#,
|
||||||
|
tag.id as i64,
|
||||||
|
tag.name,
|
||||||
|
tag.workspace_id as i64,
|
||||||
|
tag.creator_id as i64,
|
||||||
|
tag.updated_at,
|
||||||
|
tag.deleted_at,
|
||||||
|
tag.permissions,
|
||||||
|
)
|
||||||
|
.execute(&mut self.db)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_clients(&mut self) -> Result<(), AppError> {
|
||||||
|
let clients = self.toggl_api.get_clients().await?;
|
||||||
|
|
||||||
|
for client in clients {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO tracking_clients (id, updated_at, archived, creator_id, integration_provider, notes, name, server_deleted_at, workspace_id, permissions)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
|
updated_at = excluded.updated_at,
|
||||||
|
archived = excluded.archived,
|
||||||
|
creator_id = excluded.creator_id,
|
||||||
|
integration_provider = excluded.integration_provider,
|
||||||
|
notes = excluded.notes,
|
||||||
|
name = excluded.name,
|
||||||
|
server_deleted_at = excluded.server_deleted_at,
|
||||||
|
workspace_id = excluded.workspace_id,
|
||||||
|
permissions = excluded.permissions
|
||||||
|
"#,
|
||||||
|
client.id,
|
||||||
|
client.updated_at,
|
||||||
|
client.archived,
|
||||||
|
client.creator_id,
|
||||||
|
client.integration_provider,
|
||||||
|
client.notes,
|
||||||
|
client.name,
|
||||||
|
client.server_deleted_at,
|
||||||
|
client.workspace_id,
|
||||||
|
client.permissions,
|
||||||
|
)
|
||||||
|
.execute(&mut self.db)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -143,7 +265,7 @@ async fn main() {
|
|||||||
toggl_api: api,
|
toggl_api: api,
|
||||||
};
|
};
|
||||||
|
|
||||||
worker.update_projects()
|
worker.update(TimeDelta::days(7))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -362,21 +362,21 @@ pub mod types {
|
|||||||
|
|
||||||
/// The Workspace ID associated with the client.
|
/// The Workspace ID associated with the client.
|
||||||
#[serde(rename = "wid")]
|
#[serde(rename = "wid")]
|
||||||
pub workspace_id: i32,
|
pub workspace_id: i64,
|
||||||
|
|
||||||
permissions: Option<String>,
|
pub permissions: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
id: u64,
|
pub id: u64,
|
||||||
name: String,
|
pub name: String,
|
||||||
workspace_id: u64,
|
pub workspace_id: u64,
|
||||||
creator_id: u64,
|
pub creator_id: u64,
|
||||||
#[serde(rename = "at")]
|
#[serde(rename = "at")]
|
||||||
updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
deleted_at: Option<DateTime<Utc>>,
|
pub deleted_at: Option<DateTime<Utc>>,
|
||||||
permissions: Option<String>,
|
pub permissions: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
@ -482,7 +482,6 @@ pub mod types {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::Display;
|
|
||||||
|
|
||||||
impl fmt::Debug for TogglReportFilters {
|
impl fmt::Debug for TogglReportFilters {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user