Skip to content

Commit

Permalink
✨ list all migrations applied to the database
Browse files Browse the repository at this point in the history
  • Loading branch information
Odonno committed Apr 14, 2023
1 parent 6d762bd commit e8a1ceb
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 12 deletions.
55 changes: 55 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ edition = "2021"

[dependencies]
chrono = "0.4.24"
chrono-human-duration = "0.1.1"
clap = { version = "4.1.8", features = ["derive"] }
cli-table = "0.4.7"
diffy = "0.3.0"
fs_extra = "1.3.0"
regex = "1.7.1"
Expand All @@ -29,4 +31,3 @@ surrealdb = "1.0.0-beta.9"
assert_cmd = "2.0.10"
dir-diff = "0.3.2"
serial_test = "2.0.0"

10 changes: 1 addition & 9 deletions src/apply.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
use fs_extra::dir::{DirEntryAttr, DirEntryValue};
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, path::Path, process};
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};

use crate::{config, definitions};

#[derive(Serialize, Deserialize, Debug)]
struct ScriptMigration {
script_name: String,
executed_at: String,
}
use crate::{config, definitions, models::ScriptMigration};

fn within_transaction(inner_query: String) -> String {
format!(
Expand Down Expand Up @@ -71,7 +64,6 @@ pub async fn main(
}

let mut migrations_applied: Vec<ScriptMigration> = response.unwrap();

migrations_applied.sort_by_key(|m| m.executed_at.clone());

let mut config = HashSet::new();
Expand Down
40 changes: 38 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,53 @@ pub enum Action {
/// This parameter allows you to skip ulterior migrations.
#[clap(long)]
up: Option<String>,
/// Url of the surrealdb instance.
/// Default value is `localhost:8000`.
#[clap(long)]
url: Option<String>,
/// Namespace to use inside the surrealdb instance.
/// Default value is `test`.
#[clap(long)]
ns: Option<String>,
/// Name of the database to use inside the surrealdb instance.
/// Default value is `test`.
#[clap(long)]
db: Option<String>,
/// Username used to authenticate to the surrealdb instance.
/// Default value is `root`.
#[clap(short, long)]
username: Option<String>,
/// Password used to authenticate to the surrealdb instance.
/// Default value is `root`.
#[clap(short, long)]
password: Option<String>,
},
/// List all migrations applied to the database
#[clap(aliases = vec!["ls"])]
List {
/// Url of the surrealdb instance.
/// Default value is `localhost:8000`.
#[clap(long)]
url: Option<String>,
/// Namespace to use inside the surrealdb instance.
/// Default value is `test`.
#[clap(long)]
ns: Option<String>,
/// Name of the database to use inside the surrealdb instance.
/// Default value is `test`.
#[clap(long)]
db: Option<String>,
/// Username used to authenticate to the surrealdb instance.
/// Default value is `root`.
#[clap(short, long)]
username: Option<String>,
/// Password used to authenticate to the surrealdb instance.
/// Default value is `root`.
#[clap(short, long)]
password: Option<String>,
#[clap(long)]
no_color: bool,
},
}

#[derive(Subcommand, Debug)]
Expand All @@ -58,7 +94,7 @@ pub enum CreateAction {
Schema {
/// Name of the schema to generate
name: String,
/// A list of fields to define on the table
/// A list of fields to define on the table, using "," as a delimiter
#[clap(short, long, value_delimiter = ',')]
fields: Option<Vec<String>>,
#[clap(long)]
Expand All @@ -69,7 +105,7 @@ pub enum CreateAction {
Event {
/// Name of the event to generate
name: String,
/// A list of fields to define on the table
/// A list of fields to define on the table, using "," as a delimiter
#[clap(short, long, value_delimiter = ',')]
fields: Option<Vec<String>>,
#[clap(long)]
Expand Down
106 changes: 106 additions & 0 deletions src/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use chrono::{DateTime, Utc};
use chrono_human_duration::ChronoHumanDuration;
use cli_table::{format::Border, Cell, ColorChoice, Style, Table};
use std::process;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};

use crate::{config, models::ScriptMigration};

pub async fn main(
url: Option<String>,
ns: Option<String>,
db: Option<String>,
username: Option<String>,
password: Option<String>,
no_color: bool,
) {
let db_config = config::retrieve_db_config();

let url = url.or(db_config.url).unwrap_or("localhost:8000".to_owned());

let connection = Surreal::new::<Ws>(url.to_owned()).await;

if let Err(error) = connection {
eprintln!("{}", error);
process::exit(1);
}

let client = connection.unwrap();

let username = username.or(db_config.username).unwrap_or("root".to_owned());
let password = password.or(db_config.password).unwrap_or("root".to_owned());

client
.signin(Root {
username: &username,
password: &password,
})
.await
.unwrap();

let ns = ns.or(db_config.ns).unwrap_or("test".to_owned());
let db = db.or(db_config.db).unwrap_or("test".to_owned());

client
.use_ns(ns.to_owned())
.use_db(db.to_owned())
.await
.unwrap();

let response = client.select("script_migration").await;

if let Err(error) = response {
eprintln!("{}", error);
process::exit(1);
}

let mut migrations_applied: Vec<ScriptMigration> = response.unwrap();
migrations_applied.sort_by_key(|m| m.executed_at.clone());

if migrations_applied.is_empty() {
println!("No migrations applied yet!");
} else {
let now = Utc::now();

let rows = migrations_applied
.iter()
.map(|m| {
let display_name = m
.script_name
.split("_")
.skip(2)
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join("_");

let executed_at = DateTime::parse_from_rfc3339(&m.executed_at).unwrap();
let since = now.signed_duration_since(executed_at);
let since = since.format_human().to_string();

let file_name = m.script_name.clone() + ".surql";

vec![display_name.cell(), since.cell(), file_name.cell()]
})
.collect::<Vec<_>>();

let color_choice = if no_color {
ColorChoice::Never
} else {
ColorChoice::Auto
};

let table = rows
.table()
.title(vec![
"Name".cell().bold(true),
"Executed at".cell().bold(true),
"File name".cell().bold(true),
])
.color_choice(color_choice)
.border(Border::builder().build());

let table_display = table.display().unwrap();

println!("{}", table_display);
}
}
10 changes: 10 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ mod cli;
mod config;
mod create;
mod definitions;
mod list;
mod models;
mod scaffold;

#[tokio::main]
Expand Down Expand Up @@ -46,5 +48,13 @@ async fn main() {
username,
password,
} => apply::main(up, url, ns, db, username, password).await,
Action::List {
url,
ns,
db,
username,
password,
no_color,
} => list::main(url, ns, db, username, password, no_color).await,
};
}
7 changes: 7 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct ScriptMigration {
pub script_name: String,
pub executed_at: String,
}
Loading

0 comments on commit e8a1ceb

Please sign in to comment.