use async_trait::async_trait;
use crate::client::Oci;
use crate::error::{Error, Result};
use crate::services::email::models::*;
use crate::services::email::sender_trait::EmailSender;
#[derive(Clone)]
pub struct EmailDelivery {
oci_client: Oci,
submit_endpoint: String,
}
impl EmailDelivery {
pub async fn new(oci_client: Oci) -> Result<Self> {
let tenancy_id = oci_client.tenancy_id().to_string();
let region = oci_client.region().to_string();
let config =
Self::get_email_configuration_internal(&oci_client, &tenancy_id, ®ion).await?;
Ok(Self {
oci_client,
submit_endpoint: config.http_submit_endpoint,
})
}
async fn get_email_configuration_internal(
oci_client: &Oci,
compartment_id: &str,
region: &str,
) -> Result<EmailConfiguration> {
let path = format!("/20170907/configuration?compartmentId={compartment_id}");
let host = format!("ctrl.email.{region}.oci.oraclecloud.com");
let url = format!("https://{host}{path}");
let (date_header, auth_header) = oci_client
.signer()
.sign_request("GET", &path, &host, None)?;
let response = oci_client
.client()
.get(&url)
.header("host", &host)
.header("date", &date_header)
.header("authorization", &auth_header)
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let body = response.text().await?;
return Err(Error::ApiError {
code: status.to_string(),
message: body,
});
}
response.json().await.map_err(Into::into)
}
pub async fn get_email_configuration(
&self,
compartment_id: impl Into<String>,
) -> Result<EmailConfiguration> {
let compartment_id = compartment_id.into();
let region = self.oci_client.region().to_string();
Self::get_email_configuration_internal(&self.oci_client, &compartment_id, ®ion).await
}
pub async fn send(&self, email: Email) -> Result<SubmitEmailResponse> {
self.send_impl(email).await
}
async fn send_impl(&self, mut email: Email) -> Result<SubmitEmailResponse> {
let compartment_id = self.oci_client.compartment_id().to_string();
if email.sender.compartment_id.is_empty() {
email.sender.set_compartment_id(&compartment_id);
}
let path = "/20220926/actions/submitEmail";
let url = format!("https://{}{}", &self.submit_endpoint, path);
let body_json = serde_json::to_string(&email)?;
let body_sha256 = {
use base64::{Engine, engine::general_purpose};
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(body_json.as_bytes());
let result = hasher.finalize();
general_purpose::STANDARD.encode(result)
};
let (date_header, auth_header) = self.oci_client.signer().sign_request(
"POST",
path,
&self.submit_endpoint,
Some(&body_json),
)?;
let response = self
.oci_client
.client()
.post(&url)
.header("host", &self.submit_endpoint)
.header("date", &date_header)
.header("authorization", &auth_header)
.header("content-type", "application/json")
.header("content-length", body_json.len().to_string())
.header("x-content-sha256", &body_sha256)
.body(body_json)
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let body = response.text().await?;
return Err(Error::ApiError {
code: status.to_string(),
message: body,
});
}
let submit_response: SubmitEmailResponse = response.json().await?;
Ok(submit_response)
}
pub async fn list_senders(
&self,
compartment_id: impl Into<String>,
lifecycle_state: Option<&str>,
email_address: Option<&str>,
) -> Result<Vec<SenderSummary>> {
let compartment_id = compartment_id.into();
let mut query_params = vec![format!("compartmentId={}", compartment_id)];
if let Some(state) = lifecycle_state {
query_params.push(format!("lifecycleState={state}"));
}
if let Some(email) = email_address {
query_params.push(format!("emailAddress={email}"));
}
let query_string = query_params.join("&");
let path = format!("/20170907/senders?{query_string}");
let host = format!(
"ctrl.email.{}.oci.oraclecloud.com",
self.oci_client.region()
);
let url = format!("https://{host}{path}");
let (date_header, auth_header) = self
.oci_client
.signer()
.sign_request("GET", &path, &host, None)?;
let response = self
.oci_client
.client()
.get(&url)
.header("host", &host)
.header("date", &date_header)
.header("authorization", &auth_header)
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let body = response.text().await?;
return Err(Error::ApiError {
code: status.to_string(),
message: body,
});
}
let senders: Vec<SenderSummary> = response.json().await?;
Ok(senders)
}
}
#[async_trait]
impl EmailSender for EmailDelivery {
async fn send(&self, email: Email) -> Result<SubmitEmailResponse> {
self.send_impl(email).await
}
}