From d47174efee1d07440c384c7468b8894d38004cd1 Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Thu, 2 Nov 2023 16:00:41 +0000 Subject: [PATCH] (toggl-portal): Database work --- Cargo.toml | 2 ++ justfile | 4 ++++ migration/src/lib.rs | 10 +++++--- ...20231101_172500_create_time_entry_table.rs | 11 +++++---- src/db.rs | 17 ++++++++++++++ src/entity/time_entry.rs | 7 +++--- src/main.rs | 23 ++++++++++++++++++- 7 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 src/db.rs diff --git a/Cargo.toml b/Cargo.toml index d456ce5..56fe81e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,5 @@ sea-orm = { version = "0.12", features = [ ] } migration = { path = "./migration" } +chrono = { version = "0.4.31", features = ["serde"] } +futures = "0.3.29" diff --git a/justfile b/justfile index 8c35014..3acf9e6 100755 --- a/justfile +++ b/justfile @@ -1,5 +1,9 @@ #!/usr/bin/env just --justfile +reset: + sea-orm-cli migrate down + sea-orm-cli migrate up + migrate: sea-orm-cli migrate up diff --git a/migration/src/lib.rs b/migration/src/lib.rs index c644654..4007cb0 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -7,8 +7,12 @@ pub struct Migrator; #[async_trait::async_trait] impl MigratorTrait for Migrator { fn migrations() -> Vec> { - vec![ - Box::new(m20231101_172500_create_time_entry_table::Migration), - ] + vec![Box::new( + m20231101_172500_create_time_entry_table::Migration, + )] + } + + fn migration_table_name() -> DynIden { + Alias::new("toggl_portal_seaql_migrations").into_iden() } } diff --git a/migration/src/m20231101_172500_create_time_entry_table.rs b/migration/src/m20231101_172500_create_time_entry_table.rs index 092c81f..be58e44 100644 --- a/migration/src/m20231101_172500_create_time_entry_table.rs +++ b/migration/src/m20231101_172500_create_time_entry_table.rs @@ -13,15 +13,17 @@ impl MigrationTrait for Migration { .if_not_exists() .col( ColumnDef::new(TimeEntry::Id) - .big_integer() + .integer() .not_null() .auto_increment() .primary_key(), ) + .col( + ColumnDef::new(TimeEntry::TogglId).big_unsigned().not_null().unique_key()) .col(ColumnDef::new(TimeEntry::Description).string().not_null()) - .col(ColumnDef::new(TimeEntry::ProjectId).big_integer()) - .col(ColumnDef::new(TimeEntry::Start).timestamp().not_null()) - .col(ColumnDef::new(TimeEntry::Stop).timestamp().not_null()) + .col(ColumnDef::new(TimeEntry::ProjectId).big_unsigned()) + .col(ColumnDef::new(TimeEntry::Start).timestamp_with_time_zone().not_null()) + .col(ColumnDef::new(TimeEntry::Stop).timestamp_with_time_zone().not_null()) .col(ColumnDef::new(TimeEntry::RawJson).json_binary().not_null()) .to_owned(), ) @@ -39,6 +41,7 @@ impl MigrationTrait for Migration { enum TimeEntry { Table, Id, + TogglId, Description, ProjectId, Start, diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..134f625 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,17 @@ +use sea_orm::{NotSet, Set}; +use crate::entity::time_entry::ActiveModel; +use crate::types::ReportEntry; + +impl ReportEntry { + pub(crate) fn as_models(&self) -> Vec { + self.time_entries.iter().map(|inner| ActiveModel { + id: NotSet, + toggl_id: Set(inner.id as i64), + description: Set(self.description.clone()), + project_id: Set(self.project_id.map(|id| id as i64)), + start: Set(chrono::DateTime::parse_from_rfc3339(&inner.start).unwrap()), + stop: Set(chrono::DateTime::parse_from_rfc3339(&inner.start).unwrap()), + raw_json: Set(serde_json::to_value(inner).unwrap()), + }).collect() + } +} diff --git a/src/entity/time_entry.rs b/src/entity/time_entry.rs index ff4fdf8..9910f1c 100644 --- a/src/entity/time_entry.rs +++ b/src/entity/time_entry.rs @@ -7,11 +7,12 @@ use serde::{Deserialize, Serialize}; #[sea_orm(table_name = "time_entry")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, + pub id: i32, + pub toggl_id: i64, pub description: String, pub project_id: Option, - pub start: DateTime, - pub stop: DateTime, + pub start: DateTimeWithTimeZone, + pub stop: DateTimeWithTimeZone, #[sea_orm(column_type = "JsonBinary")] pub raw_json: Json, } diff --git a/src/main.rs b/src/main.rs index 43be354..2d0f368 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,11 +12,16 @@ use clap::Parser; use serde_json::Value; use std::collections::HashMap; use std::net::SocketAddr; +use sea_orm::{DatabaseConnection, EntityTrait}; +use sea_orm::sea_query::OnConflict; use tower_http::trace::TraceLayer; use migration::{Migrator, MigratorTrait}; +use crate::entity::time_entry::{self, Entity as TimeEntry}; mod client; mod types; +mod db; +mod entity; #[derive(Debug, Clone, Parser)] struct Config { @@ -35,9 +40,25 @@ struct Config { pub async fn report( Extension(toggl_client): Extension, + Extension(db): Extension, Json(query): Json, ) -> Result>> { - Ok(toggl_client.full_report(&query).await.map(Json)?) + let report = toggl_client.full_report(&query).await?; + let models = report.iter().flat_map(|entry| entry.as_models()); + + TimeEntry::insert_many(models) + .on_conflict( + OnConflict::column(time_entry::Column::TogglId) + .update_columns(vec![ + time_entry::Column::Description, + time_entry::Column::ProjectId, + time_entry::Column::Start, + time_entry::Column::Stop, + time_entry::Column::RawJson, + ]).to_owned() + ).exec(&db).await?; + + Ok(Json(report)) } pub async fn current(