From 317fb36321e402c3917e57eb924faa167d2a97ae Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sun, 21 Apr 2024 02:22:47 +0800 Subject: [PATCH] Add migrate & unmigrate core helpers (#250) --- .github/workflows/ci.yml | 11 +++-- butane/tests/migration-tests.rs | 47 ++++++++++++---------- butane_core/src/migrations/mod.rs | 30 +++++++++++++- butane_test_helper/src/lib.rs | 6 +-- docs/getting-started.md | 7 +--- examples/getting_started/src/lib.rs | 7 +--- examples/getting_started/tests/rollback.rs | 18 ++------- examples/newtype/src/lib.rs | 7 +--- examples/newtype/tests/rollback.rs | 8 +--- 9 files changed, 76 insertions(+), 65 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dd4326c..cf9e7545 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,13 +102,18 @@ jobs: run: cd butane_codegen && cargo +stable test --all-features - name: Test CLI run: cd butane_cli && cargo +stable test --all-features - - name: Run example cli tests - run: cd example && cargo +stable test --all-features - name: Test run: cd butane && cargo +stable test --all-features - - name: Check example migrations + - name: Check example migrations have been updated run: | set -ex make regenerate-example-migrations git add -A git diff --exit-code + - name: Run tests in examples + run: | + cargo +stable test -p example --all-features + cd examples + for dir in *; do + cargo +stable test -p $dir --all-features + done diff --git a/butane/tests/migration-tests.rs b/butane/tests/migration-tests.rs index 0b1bb3b7..4e21c58d 100644 --- a/butane/tests/migration-tests.rs +++ b/butane/tests/migration-tests.rs @@ -1,9 +1,8 @@ -use butane::migrations::{ - adb::DeferredSqlType, adb::TypeIdentifier, adb::TypeKey, MemMigrations, Migration, - MigrationMut, Migrations, MigrationsMut, -}; -use butane::{db::Connection, prelude::*, SqlType, SqlVal}; use butane_core::codegen::{butane_type_with_migrations, model_with_migrations}; +use butane_core::db::{BackendConnection, Connection}; +use butane_core::migrations::adb::{DeferredSqlType, TypeIdentifier, TypeKey}; +use butane_core::migrations::{MemMigrations, Migration, MigrationMut, Migrations, MigrationsMut}; +use butane_core::{SqlType, SqlVal}; #[cfg(feature = "pg")] use butane_test_helper::pg_connection; #[cfg(feature = "sqlite")] @@ -343,18 +342,21 @@ fn test_migrate( .create_migration(&backends, "v2", ms.latest().as_ref()) .unwrap()); - let mut to_apply = ms.unapplied_migrations(conn).unwrap(); + let to_apply = ms.unapplied_migrations(conn).unwrap(); assert_eq!(to_apply.len(), 2); - for m in &to_apply { - m.apply(conn).unwrap(); - } + + ms.migrate(conn).unwrap(); + + let to_apply = ms.unapplied_migrations(conn).unwrap(); + assert_eq!(to_apply.len(), 0); + verify_sql(conn, &ms, expected_up_sql, expected_down_sql); // Now downgrade, just to make sure we can - to_apply.reverse(); - for m in to_apply { - m.downgrade(conn).unwrap(); - } + ms.unmigrate(conn).unwrap(); + + let to_apply = ms.unapplied_migrations(conn).unwrap(); + assert_eq!(to_apply.len(), 2); } fn verify_sql( @@ -559,16 +561,19 @@ fn migration_delete_table(conn: &mut Connection, expected_up_sql: &str, expected .create_migration(&backends, "v2", ms.latest().as_ref()) .unwrap()); - let mut to_apply = ms.unapplied_migrations(conn).unwrap(); + let to_apply = ms.unapplied_migrations(conn).unwrap(); assert_eq!(to_apply.len(), 2); - for m in &to_apply { - m.apply(conn).unwrap(); - } + + ms.migrate(conn).unwrap(); + + let to_apply = ms.unapplied_migrations(conn).unwrap(); + assert_eq!(to_apply.len(), 0); + verify_sql(conn, &ms, expected_up_sql, expected_down_sql); // Now downgrade, just to make sure we can - to_apply.reverse(); - for m in to_apply { - m.downgrade(conn).unwrap(); - } + ms.unmigrate(conn).unwrap(); + + let to_apply = ms.unapplied_migrations(conn).unwrap(); + assert_eq!(to_apply.len(), 2); } diff --git a/butane_core/src/migrations/mod.rs b/butane_core/src/migrations/mod.rs index a7e7ff9b..4cf024b8 100644 --- a/butane_core/src/migrations/mod.rs +++ b/butane_core/src/migrations/mod.rs @@ -8,7 +8,7 @@ use std::path::Path; use fallible_iterator::FallibleIterator; use nonempty::NonEmpty; -use crate::db::BackendRows; +use crate::db::{BackendConnection, BackendRows}; use crate::db::{Column, ConnectionMethods}; use crate::sqlval::{FromSql, SqlValRef, ToSql}; use crate::{db, query, DataObject, DataResult, Error, PrimaryKeyType, Result, SqlType}; @@ -109,6 +109,34 @@ pub trait Migrations { } Ok(None) } + + /// Migrate connection forward. + fn migrate(&self, connection: &mut impl BackendConnection) -> Result<()> { + let to_apply = self.unapplied_migrations(connection)?; + for migration in &to_apply { + crate::info!("Applying migration {}", migration.name()); + migration.apply(connection)?; + } + Ok(()) + } + + /// Remove all applied migrations. + fn unmigrate(&self, connection: &mut impl BackendConnection) -> Result<()> { + let mut migration = match self.last_applied_migration(connection)? { + Some(migration) => migration, + None => return Ok(()), + }; + migration.downgrade(connection)?; + + while let Ok(Some(migration_name)) = migration.migration_from() { + migration = self + .get_migration(&migration_name) + .ok_or(Error::MigrationError("Migration not in chain".to_string()))?; + crate::info!("Rolling back migration {}", migration.name()); + migration.downgrade(connection)?; + } + Ok(()) + } } /// Extension of [`Migrations`] to modify the series of migrations. diff --git a/butane_test_helper/src/lib.rs b/butane_test_helper/src/lib.rs index 315f30e7..9bb1e8c8 100644 --- a/butane_test_helper/src/lib.rs +++ b/butane_test_helper/src/lib.rs @@ -209,11 +209,7 @@ pub fn setup_db(backend: Box, conn: &mut Connection, migrate: bool) "expected to create migration" ); log::info!("created current migration"); - let to_apply = mem_migrations.unapplied_migrations(conn).unwrap(); - for m in to_apply { - log::info!("Applying migration {}", m.name()); - m.apply(conn).unwrap(); - } + mem_migrations.migrate(conn).unwrap(); } /// Create a sqlite [`Connection`]. diff --git a/docs/getting-started.md b/docs/getting-started.md index 9c7eac8a..a288d46f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -505,14 +505,11 @@ to use these migrations: ``` rust pub fn establish_connection() -> Connection { - use butane::migrations::{Migration, Migrations}; + use butane::migrations::Migrations; let mut connection = butane::db::connect(&ConnectionSpec::load(".butane/connection.json").unwrap()).unwrap(); let migrations = butane_migrations::get_migrations().unwrap(); - let to_apply = migrations.unapplied_migrations(&connection).unwrap(); - for migration in to_apply { - migration.apply(&mut connection).unwrap(); - } + migrations.migrate(&mut connection).unwrap(); connection } ``` diff --git a/examples/getting_started/src/lib.rs b/examples/getting_started/src/lib.rs index dcacc12c..ab2b7270 100644 --- a/examples/getting_started/src/lib.rs +++ b/examples/getting_started/src/lib.rs @@ -6,7 +6,7 @@ pub mod butane_migrations; pub mod models; use butane::db::{Connection, ConnectionSpec}; -use butane::migrations::{Migration, Migrations}; +use butane::migrations::Migrations; use butane::prelude::*; use models::{Blog, Post}; @@ -15,10 +15,7 @@ pub fn establish_connection() -> Connection { let mut connection = butane::db::connect(&ConnectionSpec::load(".butane/connection.json").unwrap()).unwrap(); let migrations = butane_migrations::get_migrations().unwrap(); - let to_apply = migrations.unapplied_migrations(&connection).unwrap(); - for migration in to_apply { - migration.apply(&mut connection).unwrap(); - } + migrations.migrate(&mut connection).unwrap(); connection } diff --git a/examples/getting_started/tests/rollback.rs b/examples/getting_started/tests/rollback.rs index 19c250e0..0b14a67e 100644 --- a/examples/getting_started/tests/rollback.rs +++ b/examples/getting_started/tests/rollback.rs @@ -1,5 +1,5 @@ use butane::db::{BackendConnection, Connection}; -use butane::migrations::{Migration, Migrations}; +use butane::migrations::Migrations; use butane::DataObject; use butane_test_helper::*; @@ -38,22 +38,12 @@ fn migrate_and_rollback(mut connection: Connection) { // Migrate forward. let base_dir = std::path::PathBuf::from(".butane"); let migrations = butane_cli::get_migrations(&base_dir).unwrap(); - let to_apply = migrations.unapplied_migrations(&connection).unwrap(); - for migration in &to_apply { - migration - .apply(&mut connection) - .unwrap_or_else(|err| panic!("migration {} failed: {err}", migration.name())); - eprintln!("Applied {}", migration.name()); - } + + migrations.migrate(&mut connection).unwrap(); insert_data(&connection); // Rollback migrations. - for migration in to_apply.iter().rev() { - migration - .downgrade(&mut connection) - .unwrap_or_else(|err| panic!("rollback of {} failed: {err}", migration.name())); - eprintln!("Rolled back {}", migration.name()); - } + migrations.unmigrate(&mut connection).unwrap(); } testall_no_migrate!(migrate_and_rollback); diff --git a/examples/newtype/src/lib.rs b/examples/newtype/src/lib.rs index fd3b9ace..fdbf5c4f 100644 --- a/examples/newtype/src/lib.rs +++ b/examples/newtype/src/lib.rs @@ -6,7 +6,7 @@ pub mod butane_migrations; pub mod models; use butane::db::{Connection, ConnectionSpec}; -use butane::migrations::{Migration, Migrations}; +use butane::migrations::Migrations; use butane::prelude::*; use models::{Blog, Post}; @@ -15,10 +15,7 @@ pub fn establish_connection() -> Connection { let mut connection = butane::db::connect(&ConnectionSpec::load(".butane/connection.json").unwrap()).unwrap(); let migrations = butane_migrations::get_migrations().unwrap(); - let to_apply = migrations.unapplied_migrations(&connection).unwrap(); - for migration in to_apply { - migration.apply(&mut connection).unwrap(); - } + migrations.migrate(&mut connection).unwrap(); connection } diff --git a/examples/newtype/tests/rollback.rs b/examples/newtype/tests/rollback.rs index dbe571c7..a629518c 100644 --- a/examples/newtype/tests/rollback.rs +++ b/examples/newtype/tests/rollback.rs @@ -32,12 +32,8 @@ fn migrate_and_rollback(mut connection: Connection) { let base_dir = std::path::PathBuf::from(".butane"); let migrations = butane_cli::get_migrations(&base_dir).unwrap(); let to_apply = migrations.unapplied_migrations(&connection).unwrap(); - for migration in &to_apply { - migration - .apply(&mut connection) - .unwrap_or_else(|err| panic!("migration {} failed: {err}", migration.name())); - eprintln!("Applied {}", migration.name()); - } + + migrations.migrate(&mut connection).unwrap(); insert_data(&connection);