Skip to content

Commit

Permalink
Merge pull request #223 from loco-rs/create-database
Browse files Browse the repository at this point in the history
crete database
  • Loading branch information
jondot authored Dec 26, 2023
2 parents f38d113 + f1baf62 commit 89128d8
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 6 deletions.
1 change: 1 addition & 0 deletions examples/demo/tests/cmd/cli.trycmd
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Perform DB operations
Usage: blo-cli db [OPTIONS] <COMMAND>

Commands:
create Create schema
migrate Migrate schema (up)
reset Drop all tables, then reapply all migrations
status Migration status
Expand Down
18 changes: 15 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ cfg_if::cfg_if! {
if #[cfg(feature = "with-db")] {
use sea_orm_migration::MigratorTrait;
use crate::doctor;
use crate::boot::run_db;
use crate::boot::{run_db};
use crate::db;
use std::process::exit;
} else {}
}
Expand Down Expand Up @@ -178,6 +179,8 @@ impl From<ComponentArg> for Component {

#[derive(Subcommand)]
enum DbCommands {
/// Create schema
Create,
/// Migrate schema (up)
Migrate,
/// Drop all tables, then reapply all migrations
Expand All @@ -198,6 +201,9 @@ impl From<DbCommands> for RunDbCommand {
DbCommands::Status => Self::Status,
DbCommands::Entities => Self::Entities,
DbCommands::Truncate => Self::Truncate,
DbCommands::Create => {
unreachable!("Create db should't handled in the global db commands")
}
}
}
}
Expand Down Expand Up @@ -283,8 +289,14 @@ pub async fn main<H: Hooks, M: MigratorTrait>() -> eyre::Result<()> {
}
#[cfg(feature = "with-db")]
Commands::Db { command } => {
let app_context = create_context::<H>(&environment).await?;
run_db::<H, M>(&app_context, command.into()).await?;
if matches!(command, DbCommands::Create) {
let environment = Environment::from_str(environment.as_str())
.unwrap_or_else(|_| Environment::Any(environment.to_string()));
let _ = db::create(&environment.load()?.database.uri).await;
} else {
let app_context = create_context::<H>(&environment).await?;
run_db::<H, M>(&app_context, command.into()).await?;
}
}
Commands::Routes {} => show_list_endpoints::<H>(),
Commands::Task { name, params } => {
Expand Down
64 changes: 61 additions & 3 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
//!
//! This module defines functions and operations related to the application's
//! database interactions.
use std::{fs::File, path::Path, time::Duration};
use crate::doctor;
use duct::cmd;
use fs_err as fs;

use rrgen::Error;
use sea_orm::{
ActiveModelTrait, ConnectOptions, Database, DatabaseConnection, DbConn, EntityTrait,
IntoActiveModel,
ActiveModelTrait, ConnectOptions, ConnectionTrait, Database, DatabaseConnection, DbConn,
EntityTrait, IntoActiveModel,
};
use sea_orm_migration::MigratorTrait;
use std::{fs::File, path::Path, time::Duration};
use tracing::info;

use super::Result as AppResult;
Expand All @@ -22,6 +23,18 @@ use crate::{
errors::Error as LocoError,
};

use lazy_static::lazy_static;
use regex::Regex;

lazy_static! {
// Getting the table name from the environment configuration.
// For example:
// postgres://loco:loco@localhost:5432/loco_app
// mysql://loco:loco@localhost:3306/loco_app
// the results will be loco_app
pub static ref EXTRACT_DB_NAME: Regex = Regex::new(r"/([^/]+)$").unwrap();
}

/// converge database logic
///
/// # Errors
Expand Down Expand Up @@ -69,6 +82,30 @@ pub async fn connect(config: &config::Database) -> Result<DbConn, sea_orm::DbErr
Database::connect(opt).await
}

/// Create a new database. This functionality is currently exclusive to Postgre databases.
///
/// # Errors
///
/// Returns a [`sea_orm::DbErr`] if an error occurs during run migration up.
pub async fn create(db_uri: &str) -> AppResult<()> {
if !db_uri.starts_with("postgres://") {
return Err(LocoError::Message(
"Only Postgres databases are supported for table creation".to_string(),
));
}
let db_name = EXTRACT_DB_NAME
.captures(db_uri)
.and_then(|cap| cap.get(1).map(|db| db.as_str()))
.ok_or(Error::Message(
"The specified table name was not found in the given Postgre database URI".to_string(),
))?;

let conn = EXTRACT_DB_NAME.replace(db_uri, "/postgres").to_string();
let db = Database::connect(conn).await?;

Ok(create_postgres_database(db_name, &db).await?)
}

/// Apply migrations to the database using the provided migrator.
///
/// # Errors
Expand Down Expand Up @@ -248,3 +285,24 @@ where
pub async fn run_app_seed<H: Hooks>(db: &DatabaseConnection, path: &Path) -> AppResult<()> {
H::seed(db, path).await
}

/// Create a Postgres table from the given table name.
///
/// To create the table with `LOCO_MYSQL_TABLE_OPTIONS`
async fn create_postgres_database(
table_name: &str,
db: &DatabaseConnection,
) -> Result<(), sea_orm::DbErr> {
let with_options = std::env::var("LOCO_MYSQL_TABLE_OPTIONS")
.map_or_else(|_| "ENCODING='UTF8'".to_string(), |options| options);

let query = format!("CREATE DATABASE {table_name} WITH {with_options}");
tracing::info!(query, "creating mysql table");

db.execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
query,
))
.await?;
Ok(())
}

0 comments on commit 89128d8

Please sign in to comment.