use anyhow::{bail, Context};
use wasmer_api::backend::gql::DeployApp;
use wasmer_deploy_schema::schema::AppConfigV1;
use crate::{cmd::AsyncCliCommand, ApiOpts, ItemFormatOpts};
#[derive(clap::Parser, Debug)]
pub struct CmdAppGet {
#[clap(flatten)]
api: ApiOpts,
#[clap(flatten)]
fmt: ItemFormatOpts,
ident: Option<String>,
}
#[derive(Debug)]
enum AppIdent {
AppId(String),
NamespaceAndPackageName(String, String),
}
impl CmdAppGet {
async fn run(self) -> Result<(), anyhow::Error> {
let app = self.get_app().await?;
println!("{}", self.fmt.format.render(&app));
Ok(())
}
async fn get_app(&self) -> Result<DeployApp, anyhow::Error> {
let client = self.api.client()?;
let app_ident = if let Some(ident) = &self.ident {
if let Some((namespace, name)) = ident.split_once('/') {
AppIdent::NamespaceAndPackageName(namespace.to_string(), name.to_string())
} else {
let user = wasmer_api::backend::current_user_with_namespaces(&client, None).await?;
AppIdent::NamespaceAndPackageName(user.username, ident.clone())
}
} else {
let (config, app_config_path) = Self::get_app_config_from_current_dir()?;
eprintln!("Loaded app config from: {}", app_config_path.display());
if let Some(app_id) = config.app_id {
AppIdent::AppId(app_id)
} else {
let user_accessible_apps =
wasmer_api::backend::user_accessible_apps(&client).await?;
let owner = user_accessible_apps
.iter()
.find(|app| app.name == config.name)
.map(|app| app.owner.clone())
.context("app not found")?;
AppIdent::NamespaceAndPackageName(owner.global_name, config.name)
}
};
let app = match app_ident {
AppIdent::AppId(app_id) => wasmer_api::backend::get_app_by_id(&client, app_id).await?,
AppIdent::NamespaceAndPackageName(owner, name) => {
wasmer_api::backend::get_app(&client, owner.clone(), name.clone())
.await?
.context("app not found")?
}
};
Ok(app)
}
fn get_app_config_from_current_dir() -> Result<(AppConfigV1, std::path::PathBuf), anyhow::Error>
{
let current_dir = std::env::current_dir()?;
let app_config_path = current_dir.join(AppConfigV1::CANONICAL_FILE_NAME);
if !app_config_path.exists() || !app_config_path.is_file() {
bail!(
"Could not find app.yaml at path: '{}'.\nPlease specify an app like 'wasmer app get <namespace>/<name>' or 'wasmer app get <name>`'",
app_config_path.display()
);
}
let raw_app_config = std::fs::read_to_string(&app_config_path)
.with_context(|| format!("Could not read file '{}'", app_config_path.display()))?;
let config = AppConfigV1::parse_yaml(&raw_app_config)
.map_err(|err| anyhow::anyhow!("Could not parse app.yaml: {err:?}"))?;
Ok((config, app_config_path))
}
}
impl AsyncCliCommand for CmdAppGet {
fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> {
Box::pin(self.run())
}
}
#[derive(clap::Parser, Debug)]
pub struct CmdAppInfo {
#[clap(flatten)]
api: ApiOpts,
#[clap(flatten)]
fmt: ItemFormatOpts,
ident: Option<String>,
}
impl CmdAppInfo {
async fn run(self) -> Result<(), anyhow::Error> {
let cmd_app_get = CmdAppGet {
api: self.api,
fmt: self.fmt,
ident: self.ident,
};
let app = cmd_app_get.get_app().await?;
let app_url = app.url;
let versioned_url = app.active_version.url;
let dashboard_url = app.admin_url;
eprintln!(" App Info ");
eprintln!("> App Name: {}", app.name);
eprintln!("> App URL: {}", app_url);
eprintln!("> Versioned URL: {}", versioned_url);
eprintln!("> Admin dashboard: {}", dashboard_url);
Ok(())
}
}
impl AsyncCliCommand for CmdAppInfo {
fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> {
Box::pin(self.run())
}
}