Skip to content

Commit

Permalink
* Add specific models for environment and pipelines variables
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
tobias-richter committed Oct 11, 2024
1 parent b2cde4c commit 1feb285
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/clap_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
77 changes: 61 additions & 16 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,39 @@ pub struct DomainConfig {
#[derive(Debug, Deserialize, Serialize)]
pub struct EnvironmentsConfig {
pub id: u32,
pub variables: Vec<Variable>,
pub variables: Vec<EnvironmentVariable>,
pub domains: Option<Vec<DomainConfig>>,
}

/// Model for a pipeline's ID and all its variables that will be read from the configuration YAML
#[derive(Debug, Deserialize, Serialize)]
pub struct PipelinesConfig {
pub id: u32,
pub variables: Vec<Variable>,
pub variables: Vec<PipelineVariable>,
}

/// 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<String>,
#[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<String>,
pub value: Option<String>,
#[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
Expand All @@ -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
}
}

Expand Down Expand Up @@ -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<EnvironmentVariable>,
}

/// Struct that holds a list of variables
#[derive(Debug, Deserialize, Serialize)]
pub struct VariablesList {
pub variables: Vec<Variable>,
pub struct PipelineVariablesList {
pub variables: Vec<PipelineVariable>,
}

// Models for representing Cloud Manager pipelines and descendant objects
Expand Down
80 changes: 47 additions & 33 deletions src/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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
}
Expand All @@ -35,7 +42,7 @@ pub async fn get_env_vars(
client: &mut CloudManagerClient,
program_id: u32,
env_id: u32,
) -> Result<VariablesList, reqwest::Error> {
) -> Result<EnvironmentVariablesList, reqwest::Error> {
let request_path = format!(
"{}/api/program/{}/environment/{}/variables",
HOST_NAME, program_id, env_id
Expand All @@ -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);
Expand All @@ -71,7 +78,7 @@ pub async fn set_env_vars(
client: &mut CloudManagerClient,
program_id: u32,
env_id: u32,
variables: &[Variable],
variables: &[EnvironmentVariable],
) -> Result<StatusCode, reqwest::Error> {
let request_path = format!(
"{}/api/program/{}/environment/{}/variables",
Expand Down Expand Up @@ -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<Variable> = vec![];
let mut vars_final: Vec<EnvironmentVariable> = vec![];

// Check if the targeted environment is ready
'_retry: loop {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -263,7 +269,7 @@ pub async fn get_pipeline_vars(
client: &mut CloudManagerClient,
program_id: u32,
pipeline_id: &u32,
) -> Result<VariablesList, reqwest::Error> {
) -> Result<PipelineVariablesList, reqwest::Error> {
let request_path = format!(
"{}/api/program/{}/pipeline/{}/variables",
HOST_NAME, program_id, pipeline_id
Expand All @@ -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);
Expand All @@ -299,7 +305,7 @@ pub async fn set_pipeline_vars(
client: &mut CloudManagerClient,
program_id: u32,
pipeline_id: u32,
variables: &[Variable],
variables: &[PipelineVariable],
) -> Result<StatusCode, reqwest::Error> {
let request_path = format!(
"{}/api/program/{}/pipeline/{}/variables",
Expand Down Expand Up @@ -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| {
Expand All @@ -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<Variable> = vec![];
let mut vars_final: Vec<PipelineVariable> = vec![];

// Check if the targeted environment is ready
'_retry: loop {
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
}
}
Expand Down

0 comments on commit 1feb285

Please sign in to comment.