From 84cb5f53792829dd8bcb9b820e76ce39abb1a484 Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Mon, 6 Nov 2023 14:14:26 +0000 Subject: [PATCH] (toggl-portal): Cache clients in database --- migration/src/lib.rs | 8 ++-- .../src/m20231106_134950_create_clients.rs | 46 +++++++++++++++++++ src/db.rs | 21 +++++++-- src/entity/client.rs | 21 +++++++++ src/entity/mod.rs | 2 + src/entity/prelude.rs | 2 + src/entity/time_entry.rs | 1 + src/entity/toggl_portal_seaql_migrations.rs | 17 +++++++ src/main.rs | 25 ++++++++-- src/types.rs | 12 ++--- 10 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 migration/src/m20231106_134950_create_clients.rs create mode 100644 src/entity/client.rs create mode 100644 src/entity/toggl_portal_seaql_migrations.rs diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 4007cb0..cffbe1d 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -1,15 +1,17 @@ pub use sea_orm_migration::prelude::*; mod m20231101_172500_create_time_entry_table; +mod m20231106_134950_create_clients; 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), + Box::new(m20231106_134950_create_clients::Migration), + ] } fn migration_table_name() -> DynIden { diff --git a/migration/src/m20231106_134950_create_clients.rs b/migration/src/m20231106_134950_create_clients.rs new file mode 100644 index 0000000..62050e2 --- /dev/null +++ b/migration/src/m20231106_134950_create_clients.rs @@ -0,0 +1,46 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Client::Table) + .if_not_exists() + .col( + ColumnDef::new(Client::Id) + .integer() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Client::Name).string().not_null()) + .col(ColumnDef::new(Client::Archived).boolean().not_null()) + .col(ColumnDef::new(Client::WorkspaceId).integer().not_null()) + .col(ColumnDef::new(Client::At).timestamp_with_time_zone().not_null()) + .col(ColumnDef::new(Client::ServerDeletedAt).timestamp_with_time_zone()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Client::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum Client { + Table, + Id, + Name, + Archived, + WorkspaceId, + At, + ServerDeletedAt +} diff --git a/src/db.rs b/src/db.rs index 134f625..bc3ba77 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,10 +1,10 @@ use sea_orm::{NotSet, Set}; -use crate::entity::time_entry::ActiveModel; -use crate::types::ReportEntry; +use crate::entity::{time_entry, client}; +use crate::types::{Project, ProjectClient, ReportEntry}; impl ReportEntry { - pub(crate) fn as_models(&self) -> Vec { - self.time_entries.iter().map(|inner| ActiveModel { + pub(crate) fn as_models(&self) -> Vec { + self.time_entries.iter().map(|inner| time_entry::ActiveModel { id: NotSet, toggl_id: Set(inner.id as i64), description: Set(self.description.clone()), @@ -15,3 +15,16 @@ impl ReportEntry { }).collect() } } + +impl ProjectClient { + pub fn as_model(&self) -> client::ActiveModel { + client::ActiveModel { + id: Set(self.id), + name: Set(self.name.clone()), + archived: Set(self.archived.clone()), + workspace_id: Set(self.wid), + at: Set(self.at.clone().fixed_offset()), + server_deleted_at: Set(self.server_deleted_at.clone().map(|dt| dt.fixed_offset())), + } + } +} diff --git a/src/entity/client.rs b/src/entity/client.rs new file mode 100644 index 0000000..bad6bb5 --- /dev/null +++ b/src/entity/client.rs @@ -0,0 +1,21 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "client")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: i32, + pub name: String, + pub archived: bool, + pub workspace_id: i32, + pub at: DateTimeWithTimeZone, + pub server_deleted_at: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/entity/mod.rs b/src/entity/mod.rs index fdf78da..67366d5 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -2,4 +2,6 @@ pub mod prelude; +pub mod client; pub mod time_entry; +pub mod toggl_portal_seaql_migrations; diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 397fd0d..c0edf82 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,3 +1,5 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2 +pub use super::client::Entity as Client; pub use super::time_entry::Entity as TimeEntry; +pub use super::toggl_portal_seaql_migrations::Entity as TogglPortalSeaqlMigrations; diff --git a/src/entity/time_entry.rs b/src/entity/time_entry.rs index 9910f1c..08c56c5 100644 --- a/src/entity/time_entry.rs +++ b/src/entity/time_entry.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key)] pub id: i32, + #[sea_orm(unique)] pub toggl_id: i64, pub description: String, pub project_id: Option, diff --git a/src/entity/toggl_portal_seaql_migrations.rs b/src/entity/toggl_portal_seaql_migrations.rs new file mode 100644 index 0000000..5c4dff5 --- /dev/null +++ b/src/entity/toggl_portal_seaql_migrations.rs @@ -0,0 +1,17 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "toggl_portal_seaql_migrations")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub version: String, + pub applied_at: i64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/main.rs b/src/main.rs index 53e9cda..80e4622 100644 --- a/src/main.rs +++ b/src/main.rs @@ -86,11 +86,30 @@ pub async fn start_time_entry( } async fn projects(Extension(toggl_client): Extension) -> Result>> { - Ok(Json(toggl_client.fetch_projects().await?)) + let projects = toggl_client.fetch_projects().await?; + Ok(Json(projects)) } -async fn clients(Extension(toggl_client): Extension) -> Result>> { - Ok(Json(toggl_client.fetch_clients().await?)) +async fn clients( + Extension(db): Extension, + Extension(toggl_client): Extension +) -> Result>> { + let clients = toggl_client.fetch_clients().await?; + entity::client::Entity::insert_many(clients.iter().map(ProjectClient::as_model)) + .on_conflict( + OnConflict::column(entity::client::Column::Id) + .update_columns(vec![ + entity::client::Column::Name, + entity::client::Column::Archived, + entity::client::Column::At, + entity::client::Column::ServerDeletedAt, + entity::client::Column::WorkspaceId, + ]) + .to_owned(), + ) + .exec(&db).await?; + + Ok(Json(clients)) } async fn health(Extension(toggl_client): Extension) -> Result<&'static str> { diff --git a/src/types.rs b/src/types.rs index ffd7f24..60022fb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -61,22 +61,22 @@ pub struct Project { #[derive(Debug, Serialize, Deserialize)] pub struct ProjectClient { /// Indicates whether the client is archived or not. - archived: bool, + pub archived: bool, /// Represents the timestamp of the last update made to the client. - at: DateTime, + pub at: DateTime, /// The unique identifier for the client. - id: i32, + pub id: i32, /// The name of the client. - name: String, + pub name: String, /// Indicates the timestamp when the client was deleted. If the client is not deleted, this property will be null. - server_deleted_at: Option>, + pub server_deleted_at: Option>, /// The Workspace ID associated with the client. - wid: i32, + pub wid: i32, } #[allow(non_snake_case)]