use super::*;
pub struct FreezeDraft<'a> {
pub reason: &'a str,
pub environment: Option<&'a str>,
pub until: Option<&'a str>,
pub window: Option<&'a str>,
pub timezone: Option<&'a str>,
}
pub struct ScheduledActionDraft<'a> {
pub action: &'a str,
pub at: Option<&'a str>,
pub cron: Option<&'a str>,
pub timezone: Option<&'a str>,
pub confirm: Option<&'a str>,
}
pub struct AutoPromotionDraft<'a> {
pub from: &'a str,
pub to: &'a str,
pub services: &'a [String],
pub healthy_minutes: Option<u32>,
pub confirm: Option<&'a str>,
}
impl ApiClient {
pub async fn freeze_status(&self, project_id: &str) -> Result<Answer<FreezeStatus>> {
self.get_answer(&format!(
"/api/infra/projects/{project_id}/freezes{}",
self.environment_query('?')
))
.await
}
pub async fn freeze(
&self,
project_id: &str,
draft: &FreezeDraft<'_>,
) -> Result<Answer<FreezeAnswer>> {
self.post_answer(
&format!("/api/infra/projects/{project_id}/freezes"),
&freeze_body(draft),
)
.await
}
pub async fn lift_freeze(
&self,
project_id: &str,
freeze_id: &str,
) -> Result<Answer<FreezeAnswer>> {
self.answer(
Method::DELETE,
&format!(
"/api/infra/projects/{project_id}/freezes/{}",
utf8_percent_encode(freeze_id, NON_ALPHANUMERIC)
),
None::<&()>,
)
.await
}
pub async fn protect_environment(
&self,
project_id: &str,
environment: &str,
protect: bool,
confirm: Option<&str>,
) -> Result<Answer<ProtectionAnswer>> {
let mut body = serde_json::Map::new();
body.insert("protected".into(), protect.into());
if let Some(confirm) = confirm {
body.insert("confirm".into(), confirm.into());
}
self.answer(
Method::PUT,
&format!(
"/api/infra/projects/{project_id}/environments/{}/protection",
utf8_percent_encode(environment, NON_ALPHANUMERIC)
),
Some(&serde_json::Value::Object(body)),
)
.await
}
pub async fn scheduled_actions(
&self,
service_environment_id: &str,
) -> Result<Answer<ScheduledActionsPage>> {
self.get_answer(&format!(
"/api/infra/services/{service_environment_id}/scheduled-actions"
))
.await
}
pub async fn schedule_action(
&self,
service_environment_id: &str,
draft: &ScheduledActionDraft<'_>,
) -> Result<Answer<ScheduledActionAnswer>> {
self.post_answer(
&format!("/api/infra/services/{service_environment_id}/scheduled-actions"),
&action_body(draft),
)
.await
}
pub async fn remove_scheduled_action(
&self,
service_environment_id: &str,
action_id: &str,
) -> Result<serde_json::Value> {
self.delete_for(&format!(
"/api/infra/services/{service_environment_id}/scheduled-actions/{}",
utf8_percent_encode(action_id, NON_ALPHANUMERIC)
))
.await
}
pub async fn auto_promotions(&self, project_id: &str) -> Result<Answer<AutoPromotionsPage>> {
self.get_answer(&format!("/api/infra/projects/{project_id}/auto-promotions"))
.await
}
pub async fn add_auto_promotion(
&self,
project_id: &str,
draft: &AutoPromotionDraft<'_>,
) -> Result<Answer<AutoPromotionAnswer>> {
self.post_answer(
&format!("/api/infra/projects/{project_id}/auto-promotions"),
&rule_body(draft),
)
.await
}
pub async fn remove_auto_promotion(
&self,
project_id: &str,
rule_id: &str,
) -> Result<serde_json::Value> {
self.delete_for(&format!(
"/api/infra/projects/{project_id}/auto-promotions/{}",
utf8_percent_encode(rule_id, NON_ALPHANUMERIC)
))
.await
}
}
fn freeze_body(draft: &FreezeDraft<'_>) -> serde_json::Value {
let mut body = serde_json::Map::new();
body.insert("reason".into(), draft.reason.into());
for (key, value) in [
("environment", draft.environment),
("until", draft.until),
("window", draft.window),
("timezone", draft.timezone),
] {
if let Some(value) = value {
body.insert(key.into(), value.into());
}
}
serde_json::Value::Object(body)
}
fn action_body(draft: &ScheduledActionDraft<'_>) -> serde_json::Value {
let mut body = serde_json::Map::new();
body.insert("action".into(), draft.action.into());
for (key, value) in [
("at", draft.at),
("cron", draft.cron),
("timezone", draft.timezone),
("confirm", draft.confirm),
] {
if let Some(value) = value {
body.insert(key.into(), value.into());
}
}
serde_json::Value::Object(body)
}
fn rule_body(draft: &AutoPromotionDraft<'_>) -> serde_json::Value {
let mut body = serde_json::Map::new();
body.insert("from".into(), draft.from.into());
body.insert("to".into(), draft.to.into());
if !draft.services.is_empty() {
body.insert("services".into(), draft.services.to_vec().into());
}
if let Some(minutes) = draft.healthy_minutes {
body.insert("healthyMinutes".into(), minutes.into());
}
if let Some(confirm) = draft.confirm {
body.insert("confirm".into(), confirm.into());
}
serde_json::Value::Object(body)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_should_send_only_the_reason_for_a_freeze_from_now() {
let draft = FreezeDraft {
reason: "Black Friday",
environment: None,
until: None,
window: None,
timezone: None,
};
assert_eq!(
freeze_body(&draft),
serde_json::json!({ "reason": "Black Friday" })
);
}
#[test]
fn it_should_send_a_recurring_window_with_its_environment() {
let draft = FreezeDraft {
reason: "Fin de semana",
environment: Some("production"),
until: None,
window: Some("V 18:00 - L 08:00"),
timezone: None,
};
assert_eq!(
freeze_body(&draft),
serde_json::json!({ "reason": "Fin de semana", "environment": "production", "window": "V 18:00 - L 08:00" })
);
}
#[test]
fn it_should_leave_the_services_out_of_a_rule_for_all_of_them() {
let draft = AutoPromotionDraft {
from: "staging",
to: "production",
services: &[],
healthy_minutes: Some(15),
confirm: None,
};
assert_eq!(
rule_body(&draft),
serde_json::json!({ "from": "staging", "to": "production", "healthyMinutes": 15 })
);
}
}