From 1feb285eb9eefa22f58f8a1969047ec08e38aab2 Mon Sep 17 00:00:00 2001 From: Tobias Richter Date: Fri, 11 Oct 2024 20:31:48 +0200 Subject: [PATCH] * Add specific models for environment and pipelines variables * add dry run mode pipeline vars * ensure default service value is not serialized for env-vars * ensure that service type of pipeline variables is also evaluated in PartialEq --- src/clap_app.rs | 2 +- src/models.rs | 77 ++++++++++++++++++++++++++++++++++++---------- src/variables.rs | 80 ++++++++++++++++++++++++++++-------------------- 3 files changed, 109 insertions(+), 50 deletions(-) diff --git a/src/clap_app.rs b/src/clap_app.rs index 7ce79d1..77505cc 100644 --- a/src/clap_app.rs +++ b/src/clap_app.rs @@ -207,7 +207,7 @@ pub async fn init_cli() { { if let PipelineVarsCommands::Set { input } = &pipeline_vars_command { println!("🚀 Patching pipeline variables from input file {}\n", input); - set_pipeline_vars_from_file(input, &mut cm_client, cli.ci_mode).await; + set_pipeline_vars_from_file(input, &mut cm_client, cli.ci_mode, cli.dry_run_mode).await; process::exit(0); } } diff --git a/src/models.rs b/src/models.rs index c5d7f5f..c09377b 100644 --- a/src/models.rs +++ b/src/models.rs @@ -31,7 +31,7 @@ pub struct DomainConfig { #[derive(Debug, Deserialize, Serialize)] pub struct EnvironmentsConfig { pub id: u32, - pub variables: Vec, + pub variables: Vec, pub domains: Option>, } @@ -39,21 +39,31 @@ pub struct EnvironmentsConfig { #[derive(Debug, Deserialize, Serialize)] pub struct PipelinesConfig { pub id: u32, - pub variables: Vec, + pub variables: Vec, } /// Model for all information about a Cloud Manager environment variable #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Variable { +pub struct PipelineVariable { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub value: Option, #[serde(rename(deserialize = "type", serialize = "type"))] pub variable_type: VariableType, - #[serde(default = "VariableServiceType::all")] - pub service: VariableServiceType, + #[serde(default = "PipelineVariableServiceType::default")] + pub service: PipelineVariableServiceType +} + +/// Model for all information about a Cloud Manager environment variable +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct EnvironmentVariable { + pub name: String, #[serde(skip_serializing_if = "Option::is_none")] - pub status: Option, + pub value: Option, + #[serde(rename(deserialize = "type", serialize = "type"))] + pub variable_type: VariableType, + #[serde(default = "EnvironmentVariableServiceType::default", skip_serializing_if = "env_var_service_type_is_default")] + pub service: EnvironmentVariableServiceType } /// Possible types that a variable can have @@ -64,26 +74,48 @@ pub enum VariableType { SecretString, } -/// Possible types that a service can have +/// Possible service types that an environment variable can have #[derive(Clone, Debug, Deserialize, Serialize, IntoStaticStr, EnumString, PartialEq, Eq)] #[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] -pub enum VariableServiceType { +pub enum EnvironmentVariableServiceType { All, Author, Publish, Preview, } -impl fmt::Display for VariableServiceType { +impl fmt::Display for EnvironmentVariableServiceType { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", format!("{:?}", self).to_lowercase()) + } +} +fn env_var_service_type_is_default(t: &EnvironmentVariableServiceType) -> bool { + *t == EnvironmentVariableServiceType::All +} + +impl EnvironmentVariableServiceType { + fn default() -> Self { + EnvironmentVariableServiceType::All + } +} +/// Possible service types that an environment variable can have +#[derive(Clone, Debug, Deserialize, Serialize, IntoStaticStr, EnumString, PartialEq, Eq)] +#[strum(serialize_all = "lowercase")] +#[serde(rename_all = "lowercase")] +pub enum PipelineVariableServiceType { + Build, +} + +impl fmt::Display for crate::models::PipelineVariableServiceType { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "{}", format!("{:?}", self).to_lowercase()) } } -impl VariableServiceType { - fn all() -> Self { - VariableServiceType::All +impl PipelineVariableServiceType { + fn default() -> Self { + PipelineVariableServiceType::Build } } @@ -163,15 +195,28 @@ pub struct Environment { /// Struct to serialize the response of requesting /api/program/{id}/environment/{id}/variables #[derive(Debug, Deserialize, Serialize)] -pub struct VariablesResponse { +pub struct EnvironmentVariablesResponse { + #[serde(rename(deserialize = "_embedded", serialize = "_embedded"))] + pub variables_list: EnvironmentVariablesList, +} + +/// Struct to serialize the response of requesting /api/program/{id}/environment/{id}/variables +#[derive(Debug, Deserialize, Serialize)] +pub struct PipelineVariablesResponse { #[serde(rename(deserialize = "_embedded", serialize = "_embedded"))] - pub variables_list: VariablesList, + pub variables_list: PipelineVariablesList, +} + +/// Struct that holds a list of variables +#[derive(Debug, Deserialize, Serialize)] +pub struct EnvironmentVariablesList { + pub variables: Vec, } /// Struct that holds a list of variables #[derive(Debug, Deserialize, Serialize)] -pub struct VariablesList { - pub variables: Vec, +pub struct PipelineVariablesList { + pub variables: Vec, } // Models for representing Cloud Manager pipelines and descendant objects diff --git a/src/variables.rs b/src/variables.rs index 6dfa04d..84db5f9 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -2,7 +2,7 @@ use crate::client::{AdobeConnector, CloudManagerClient}; use crate::encryption::decrypt; use crate::environments::get_environment; use crate::errors::throw_adobe_api_error; -use crate::models::{Variable, VariableType, VariablesList, VariablesResponse, YamlConfig}; +use crate::models::{EnvironmentVariable, PipelineVariable, VariableType, EnvironmentVariablesList, EnvironmentVariablesResponse, YamlConfig, PipelineVariablesList, PipelineVariablesResponse}; use crate::pipelines::get_pipeline; use crate::HOST_NAME; use colored::*; @@ -11,8 +11,15 @@ use std::process; use std::thread::sleep; use std::time::Duration; -// Make variables comparable - if they have the same name and same service they are equal. -impl PartialEq for Variable { +// Make environment variables comparable - if they have the same name and same service they are equal. +impl PartialEq for EnvironmentVariable { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.service == other.service + } +} + +// Make pipeline variables comparable - if they have the same name they are equal. +impl PartialEq for PipelineVariable { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.service == other.service } @@ -35,7 +42,7 @@ pub async fn get_env_vars( client: &mut CloudManagerClient, program_id: u32, env_id: u32, -) -> Result { +) -> Result { let request_path = format!( "{}/api/program/{}/environment/{}/variables", HOST_NAME, program_id, env_id @@ -45,7 +52,7 @@ pub async fn get_env_vars( .await? .text() .await?; - let variables: VariablesResponse = + let variables: EnvironmentVariablesResponse = serde_json::from_str(response.as_str()).unwrap_or_else(|_| { throw_adobe_api_error(response); process::exit(1); @@ -71,7 +78,7 @@ pub async fn set_env_vars( client: &mut CloudManagerClient, program_id: u32, env_id: u32, - variables: &[Variable], + variables: &[EnvironmentVariable], ) -> Result { let request_path = format!( "{}/api/program/{}/environment/{}/variables", @@ -125,7 +132,7 @@ pub async fn set_env_vars_from_file( // The vector that holds the final variables that will be set or deleted. Will be constructed // by comparing the variables that are currently set in Cloud Manager and those in the local // YAML config file. - let mut vars_final: Vec = vec![]; + let mut vars_final: Vec = vec![]; // Check if the targeted environment is ready '_retry: loop { @@ -177,12 +184,11 @@ pub async fn set_env_vars_from_file( let vars_cloud = get_env_vars(client, p.id, e.id).await.unwrap().variables; for vc in vars_cloud { if !vars_yaml.clone().contains(&vc) { - let variable_to_be_deleted = Variable { + let variable_to_be_deleted = EnvironmentVariable { name: vc.name, value: None, variable_type: vc.variable_type, - service: vc.service, - status: None, + service: vc.service }; vars_final.push(variable_to_be_deleted); } @@ -263,7 +269,7 @@ pub async fn get_pipeline_vars( client: &mut CloudManagerClient, program_id: u32, pipeline_id: &u32, -) -> Result { +) -> Result { let request_path = format!( "{}/api/program/{}/pipeline/{}/variables", HOST_NAME, program_id, pipeline_id @@ -273,7 +279,7 @@ pub async fn get_pipeline_vars( .await? .text() .await?; - let variables: VariablesResponse = + let variables: PipelineVariablesResponse = serde_json::from_str(response.as_str()).unwrap_or_else(|_| { throw_adobe_api_error(response); process::exit(1); @@ -299,7 +305,7 @@ pub async fn set_pipeline_vars( client: &mut CloudManagerClient, program_id: u32, pipeline_id: u32, - variables: &[Variable], + variables: &[PipelineVariable], ) -> Result { let request_path = format!( "{}/api/program/{}/pipeline/{}/variables", @@ -331,6 +337,7 @@ pub async fn set_pipeline_vars_from_file( file_path: &str, client: &mut CloudManagerClient, ci_mode: bool, + dry_run: bool, ) { let input = std::fs::read_to_string(file_path).expect("Unable to read file"); let input: YamlConfig = serde_yaml::from_str(input.as_str()).unwrap_or_else(|err| { @@ -352,7 +359,7 @@ pub async fn set_pipeline_vars_from_file( // The vector that holds the final variables that will be set or deleted. Will be constructed // by comparing the variables that are currently set in Cloud Manager and those in the local // YAML config file. - let mut vars_final: Vec = vec![]; + let mut vars_final: Vec = vec![]; // Check if the targeted environment is ready '_retry: loop { @@ -407,12 +414,11 @@ pub async fn set_pipeline_vars_from_file( .variables; for vc in vars_cloud { if !vars_yaml.clone().contains(&vc) { - let variable_to_be_deleted = Variable { + let variable_to_be_deleted = PipelineVariable { name: vc.name, value: None, variable_type: vc.variable_type, - service: vc.service, - status: None, + service: vc.service }; vars_final.push(variable_to_be_deleted); } @@ -429,25 +435,33 @@ pub async fn set_pipeline_vars_from_file( } } - match set_pipeline_vars(client, p.id, l.id, &vars_final).await { - Ok(status) => match status { - StatusCode::NO_CONTENT => { - println!("{:>8} Success", "✔"); - } - _ => { - eprintln!( - "{:>8} {}", - "Error, check output above".red(), - "❌".red() - ); - process::exit(2); + if dry_run { + println!( + "{:>8} --dry-run detected. Not performing any actions.", + "⚠️", + ); + } else { + match set_pipeline_vars(client, p.id, l.id, &vars_final).await { + Ok(status) => match status { + StatusCode::NO_CONTENT => { + println!("{:>8} Success", "✔"); + } + _ => { + eprintln!( + "{:>8} {}", + "Error, check output above".red(), + "❌".red() + ); + process::exit(2); + } + }, + Err(error) => { + eprintln!("{} {}", "❌ API error: ".red().bold(), error); + process::exit(1); } - }, - Err(error) => { - eprintln!("{} {}", "❌ API error: ".red().bold(), error); - process::exit(1); } } + break '_retry; } }