(toggl-portal): Database work

This commit is contained in:
Joshua Coles 2023-11-02 16:00:41 +00:00
parent 8f39bf7d23
commit d47174efee
7 changed files with 63 additions and 11 deletions

View File

@ -28,3 +28,5 @@ sea-orm = { version = "0.12", features = [
] }
migration = { path = "./migration" }
chrono = { version = "0.4.31", features = ["serde"] }
futures = "0.3.29"

View File

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

View File

@ -7,8 +7,12 @@ pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
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()
}
}

View File

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

17
src/db.rs Normal file
View File

@ -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<ActiveModel> {
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()
}
}

View File

@ -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<i64>,
pub start: DateTime,
pub stop: DateTime,
pub start: DateTimeWithTimeZone,
pub stop: DateTimeWithTimeZone,
#[sea_orm(column_type = "JsonBinary")]
pub raw_json: Json,
}

View File

@ -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<TogglClient>,
Extension(db): Extension<DatabaseConnection>,
Json(query): Json<TogglQuery>,
) -> Result<Json<Vec<ReportEntry>>> {
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(