pub mod http;
use anyhow::{ensure, Context as AnyhyowContext, Result};
use hop::{Hop, HopOptions};
use self::http::HttpClient;
use crate::commands::auth::login::util::{token_options, TokenType};
use crate::commands::ignite::groups::utils::fetch_grouped_deployments;
use crate::commands::ignite::types::Deployment;
use crate::commands::ignite::utils::{get_all_deployments, get_deployment};
use crate::config::EXEC_NAME;
use crate::store::auth::Auth;
use crate::store::context::Context;
use crate::store::Store;
#[derive(Debug)]
pub struct State {
pub is_ci: bool,
pub auth: Auth,
pub ctx: Context,
pub http: HttpClient,
pub debug: bool,
pub hop: Hop,
token: Option<String>,
token_type: Option<TokenType>,
}
#[derive(Debug, Default)]
pub struct StateOptions {
pub override_project: Option<String>,
pub override_token: Option<String>,
pub debug: bool,
}
impl State {
pub async fn new(options: StateOptions) -> Result<Self> {
let auth = Auth::new().await?;
let mut ctx = Context::new().await?;
ctx.project_override = options
.override_project
.or_else(|| ctx.default_project.clone());
let init_token = if let Some(override_token) = options.override_token {
Some(override_token)
} else if let Some(ref user) = ctx.default_user {
auth.authorized.get(user).map(|x| x.to_string())
} else {
None
};
let (token, token_type) = Self::handle_token(init_token)?;
let api_url = std::env::var("API_URL")
.ok()
.or_else(|| ctx.override_api_url.clone());
let http = HttpClient::new(token.clone(), api_url.clone());
let hop = Hop::new_with_options(HopOptions {
token: token.clone(),
api_url,
})?;
Ok(State {
is_ci: Self::check_if_ci(),
token_type,
token,
http,
hop,
auth,
ctx,
debug: options.debug,
})
}
fn handle_token(token: Option<String>) -> Result<(Option<String>, Option<TokenType>)> {
let token_type = token
.as_ref()
.map(|token| TokenType::from_token(token).context("Invalid token type"))
.transpose()?;
Ok((token, token_type))
}
fn check_if_ci() -> bool {
std::env::vars().any(|(key, _)| {
matches!(
key.as_str(),
"BUILD_NUMBER"
| "CONTINUOUS_INTEGRATION"
| "GITLAB_CI"
| "CIRCLECI"
| "APPVEYOR"
| "RUN_ID"
| "CI"
)
})
}
pub async fn login(&mut self, token: Option<String>) -> Result<()> {
ensure!(
token.is_some() || self.token.is_some(),
"You are not logged in. Please run `{} auth login` first.",
EXEC_NAME
);
if let Some(token) = token {
let (token, token_type) = Self::handle_token(Some(token))?;
self.token = token.clone();
self.token_type = token_type;
self.http = HttpClient::new(token, self.ctx.override_api_url.clone());
}
let response = token_options(self.http.clone(), self.token_type.clone()).await?;
if !response.email_verified {
log::warn!("You need to verify your email address before you can use Hop.")
}
self.ctx.current = Some(response);
if let Some(TokenType::Ptk) = self.token_type {
self.ctx.project_override = self.ctx.current.as_ref().map(|cur| cur.id.clone())
}
Ok(())
}
pub fn token(&self) -> Option<String> {
self.token.clone()
}
pub async fn get_deployment_by_name_or_id(&self, name_or_id: &str) -> Result<Deployment> {
if name_or_id.starts_with("deployment_") {
return get_deployment(&self.http, name_or_id).await;
}
let deployments =
get_all_deployments(&self.http, &self.ctx.current_project_error()?.id).await?;
let deployment = deployments
.into_iter()
.find(|d| d.name == name_or_id)
.context("Deployment not found")?;
Ok(deployment)
}
pub async fn get_deployment_by_opt_name_or_id(
&self,
name_or_id: Option<&str>,
) -> Result<Deployment> {
if let Some(name_or_id) = name_or_id {
self.get_deployment_by_name_or_id(name_or_id).await
} else {
let (deployments_fmt, deployments, validator) =
fetch_grouped_deployments(self, false, true).await?;
let idx = loop {
let idx = dialoguer::Select::new()
.with_prompt("Select a deployment")
.items(&deployments_fmt)
.default(0)
.interact()?;
if let Ok(idx) = validator(idx) {
break idx;
}
console::Term::stderr().clear_last_lines(1)?
};
Ok(deployments[idx].clone())
}
}
}