From 8f39bf7d239ae98b60dde86feb295971c5675426 Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Wed, 1 Nov 2023 17:32:26 +0000 Subject: [PATCH] Initial work re-implementing model for TimeEntries --- .env | 1 + Cargo.toml | 10 ++++ justfile | 13 +++++ migration/Cargo.toml | 19 ++++++++ migration/README.md | 41 ++++++++++++++++ migration/src/lib.rs | 14 ++++++ ...20231101_172500_create_time_entry_table.rs | 47 +++++++++++++++++++ migration/src/main.rs | 6 +++ src/entity/mod.rs | 5 ++ src/entity/prelude.rs | 3 ++ src/entity/time_entry.rs | 22 +++++++++ src/main.rs | 13 +++++ 12 files changed, 194 insertions(+) create mode 100644 .env create mode 100755 justfile create mode 100644 migration/Cargo.toml create mode 100644 migration/README.md create mode 100644 migration/src/lib.rs create mode 100644 migration/src/m20231101_172500_create_time_entry_table.rs create mode 100644 migration/src/main.rs create mode 100644 src/entity/mod.rs create mode 100644 src/entity/prelude.rs create mode 100644 src/entity/time_entry.rs diff --git a/.env b/.env new file mode 100644 index 0000000..e733ce7 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL=postgres://postgres@localhost:5432/toggl_portal diff --git a/Cargo.toml b/Cargo.toml index e4d1d59..d456ce5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,13 @@ hyper = "0.14.27" reqwest = { version = "0.11.20", features = ["rustls", "json"] } base64 = "0.21.4" serde_with = "3.3.0" + +sea-orm = { version = "0.12", features = [ + "sqlx-postgres", + "runtime-tokio-rustls", + "macros", + "with-chrono", + "with-json", +] } + +migration = { path = "./migration" } diff --git a/justfile b/justfile new file mode 100755 index 0000000..8c35014 --- /dev/null +++ b/justfile @@ -0,0 +1,13 @@ +#!/usr/bin/env just --justfile + +migrate: + sea-orm-cli migrate up + +generate-entity: + sea-orm-cli generate entity --with-serde both -o src/entity + +release: + cargo build --release + +lint: + cargo clippy diff --git a/migration/Cargo.toml b/migration/Cargo.toml new file mode 100644 index 0000000..9b9195e --- /dev/null +++ b/migration/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +async-std = { version = "1", features = ["attributes", "tokio1"] } + +[dependencies.sea-orm-migration] +version = "0.12.0" +features = [ + "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature + "sqlx-postgres", # `DATABASE_DRIVER` feature +] diff --git a/migration/README.md b/migration/README.md new file mode 100644 index 0000000..3b438d8 --- /dev/null +++ b/migration/README.md @@ -0,0 +1,41 @@ +# Running Migrator CLI + +- Generate a new migration file + ```sh + cargo run -- generate MIGRATION_NAME + ``` +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/migration/src/lib.rs b/migration/src/lib.rs new file mode 100644 index 0000000..c644654 --- /dev/null +++ b/migration/src/lib.rs @@ -0,0 +1,14 @@ +pub use sea_orm_migration::prelude::*; + +mod m20231101_172500_create_time_entry_table; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20231101_172500_create_time_entry_table::Migration), + ] + } +} diff --git a/migration/src/m20231101_172500_create_time_entry_table.rs b/migration/src/m20231101_172500_create_time_entry_table.rs new file mode 100644 index 0000000..092c81f --- /dev/null +++ b/migration/src/m20231101_172500_create_time_entry_table.rs @@ -0,0 +1,47 @@ +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(TimeEntry::Table) + .if_not_exists() + .col( + ColumnDef::new(TimeEntry::Id) + .big_integer() + .not_null() + .auto_increment() + .primary_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::RawJson).json_binary().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(TimeEntry::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum TimeEntry { + Table, + Id, + Description, + ProjectId, + Start, + Stop, + RawJson +} diff --git a/migration/src/main.rs b/migration/src/main.rs new file mode 100644 index 0000000..c6b6e48 --- /dev/null +++ b/migration/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::prelude::*; + +#[async_std::main] +async fn main() { + cli::run_cli(migration::Migrator).await; +} diff --git a/src/entity/mod.rs b/src/entity/mod.rs new file mode 100644 index 0000000..fdf78da --- /dev/null +++ b/src/entity/mod.rs @@ -0,0 +1,5 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2 + +pub mod prelude; + +pub mod time_entry; diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs new file mode 100644 index 0000000..397fd0d --- /dev/null +++ b/src/entity/prelude.rs @@ -0,0 +1,3 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2 + +pub use super::time_entry::Entity as TimeEntry; diff --git a/src/entity/time_entry.rs b/src/entity/time_entry.rs new file mode 100644 index 0000000..ff4fdf8 --- /dev/null +++ b/src/entity/time_entry.rs @@ -0,0 +1,22 @@ +//! `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 = "time_entry")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + pub description: String, + pub project_id: Option, + pub start: DateTime, + pub stop: DateTime, + #[sea_orm(column_type = "JsonBinary")] + pub raw_json: Json, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/main.rs b/src/main.rs index a49dfd9..43be354 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use serde_json::Value; use std::collections::HashMap; use std::net::SocketAddr; use tower_http::trace::TraceLayer; +use migration::{Migrator, MigratorTrait}; mod client; mod types; @@ -27,6 +28,9 @@ struct Config { #[arg(long = "addr", short, env, default_value = "0.0.0.0:3000")] address: SocketAddr, + + #[arg(long = "db", short, env)] + database_url: String, } pub async fn report( @@ -70,6 +74,14 @@ async fn main() -> Result<()> { &STANDARD.encode(&format!("{}:api_token", config.toggl_api_token)), ); + let db = sea_orm::Database::connect(config.database_url) + .await + .unwrap(); + + Migrator::up(&db, None) + .await + .expect("Failed to migrate"); + // build our application with a route let app = Router::new() .route("/health", get(health)) @@ -77,6 +89,7 @@ async fn main() -> Result<()> { .route("/report", post(report)) .route("/start_time_entry", post(start_time_entry)) .layer(Extension(toggl_client)) + .layer(Extension(db)) .layer(TraceLayer::new_for_http()); tracing::info!("Listening on {}", config.address);