use crate::api::project;
use crate::error::Error;
use crate::migrations::Migrations;
use crate::runner::{Runnable, Runner};
use crate::writer::Writer;
use eyre::Context;
use project::sqldb::connect::Request;
use serde_json::json;
use std::path::PathBuf;
#[derive(clap::Args, Clone)]
pub(crate) struct ApplyCommand {
#[arg(short, long, value_name = "PATH", default_value = "migrations")]
path: String,
#[arg(long)]
project: Option<PathBuf>,
#[arg(long)]
org: Option<String>,
}
impl Runnable for ApplyCommand {
fn runner(&self, writer: &Writer) -> impl Runner {
ApplyRunner {
command: self.clone(),
writer,
}
}
}
struct ApplyRunner<'a> {
command: ApplyCommand,
writer: &'a Writer,
}
impl<'a> Runner for ApplyRunner<'a> {
async fn run(&mut self) -> Result<(), Error> {
let mut project = self.project(&self.command.project).await?;
let client = self.api_client().await?;
let migrations_path = project.path.join(&self.command.path);
if self.command.org.is_some() {
project = project.with_org(self.command.org.as_deref());
}
self.writer.text(&format!(
"{} {} {}...\n\n",
console::style("Applying migrations").green().bold(),
console::style("from").dim(),
console::style(format!("{}", migrations_path.to_string_lossy()))
.underlined()
.bold(),
))?;
let response = client
.request::<_, project::sqldb::connect::Response>(
"/stack/sqldb/connect",
Request { project },
)
.await
.wrap_err("Failed to get SQL DB connection string")
.map_err(|e| self.server_error(Some(e.into())))?;
let connection = sqlx::PgPool::connect(&response.connection_string)
.await
.map_err(|e| self.server_error(Some(e.into())))?;
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS schema_migrations (
id VARCHAR(255) PRIMARY KEY,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
"#,
)
.execute(&connection)
.await
.map_err(|e| self.server_error(Some(e.into())))?;
let migrations = Migrations::new(migrations_path.as_path(), self.writer)
.map_err(|e| self.error(None, None, Some(e.into())))?;
migrations
.apply(response.connection_string)
.await
.wrap_err("Failed to apply migrations")
.map_err(|e| self.server_error(Some(e.into())))?;
self.writer
.text(&format!("\n{}\n", console::style("Done").green().bold()))?;
self.writer.json(json!({"success": true}))?;
Ok(())
}
}