oci_api/services/email/
client.rs1use async_trait::async_trait;
4use reqwest::Method;
5
6use crate::client::Oci;
7use crate::client::request_executor::{RequestPayload, RequestTarget};
8use crate::error::Result;
9use crate::services::email::models::*;
10use crate::services::email::sender_trait::EmailSender;
11
12#[derive(Clone)]
14pub struct EmailDelivery {
15 oci_client: Oci,
17
18 submit_endpoint: String,
20}
21
22impl EmailDelivery {
23 fn control_host_for(region: &str, realm_domain: &str) -> String {
24 format!("ctrl.email.{region}.oci.{realm_domain}")
25 }
26
27 pub async fn new(oci_client: Oci) -> Result<Self> {
34 let tenancy_id = oci_client.tenancy_id().to_string();
36 let region = oci_client.region().to_string();
37
38 let config =
40 Self::get_email_configuration_internal(&oci_client, &tenancy_id, ®ion).await?;
41
42 Ok(Self {
43 oci_client,
44 submit_endpoint: config.http_submit_endpoint,
45 })
46 }
47
48 async fn get_email_configuration_internal(
50 oci_client: &Oci,
51 compartment_id: &str,
52 region: &str,
53 ) -> Result<EmailConfiguration> {
54 let path = format!("/20170907/configuration?compartmentId={compartment_id}");
55 let host = Self::control_host_for(region, oci_client.realm_domain());
56 let response = oci_client
57 .executor()
58 .execute(
59 Method::GET,
60 RequestTarget {
61 scheme: "https",
62 host: &host,
63 path: &path,
64 },
65 RequestPayload {
66 body: None,
67 content_type: None,
68 extra_headers: Vec::new(),
69 },
70 )
71 .await?;
72 response.json().await.map_err(Into::into)
73 }
74
75 pub async fn get_email_configuration(
80 &self,
81 compartment_id: impl Into<String>,
82 ) -> Result<EmailConfiguration> {
83 let compartment_id = compartment_id.into();
84 let region = self.oci_client.region().to_string();
85 Self::get_email_configuration_internal(&self.oci_client, &compartment_id, ®ion).await
86 }
87
88 pub async fn send(&self, email: Email) -> Result<SubmitEmailResponse> {
96 self.send_impl(email).await
97 }
98
99 async fn send_impl(&self, mut email: Email) -> Result<SubmitEmailResponse> {
101 let compartment_id = self.oci_client.compartment_id().to_string();
103
104 if email.sender.compartment_id.is_empty() {
106 email.sender.set_compartment_id(&compartment_id);
107 }
108
109 let path = "/20220926/actions/submitEmail";
110 let body_json = serde_json::to_string(&email)?;
111 let response = self
112 .oci_client
113 .executor()
114 .execute(
115 Method::POST,
116 RequestTarget {
117 scheme: "https",
118 host: &self.submit_endpoint,
119 path,
120 },
121 RequestPayload {
122 body: Some(body_json),
123 content_type: Some("application/json"),
124 extra_headers: Vec::new(),
125 },
126 )
127 .await?;
128 response.json().await.map_err(Into::into)
129 }
130
131 pub async fn list_senders(
138 &self,
139 compartment_id: impl Into<String>,
140 lifecycle_state: Option<&str>,
141 email_address: Option<&str>,
142 ) -> Result<Vec<SenderSummary>> {
143 let compartment_id = compartment_id.into();
144
145 let mut query_params = vec![format!("compartmentId={}", compartment_id)];
147
148 if let Some(state) = lifecycle_state {
149 query_params.push(format!("lifecycleState={state}"));
150 }
151
152 if let Some(email) = email_address {
153 query_params.push(format!("emailAddress={email}"));
154 }
155
156 let query_string = query_params.join("&");
157 let path = format!("/20170907/senders?{query_string}");
158 let host = Self::control_host_for(self.oci_client.region(), self.oci_client.realm_domain());
159 let response = self
160 .oci_client
161 .executor()
162 .execute(
163 Method::GET,
164 RequestTarget {
165 scheme: "https",
166 host: &host,
167 path: &path,
168 },
169 RequestPayload {
170 body: None,
171 content_type: None,
172 extra_headers: Vec::new(),
173 },
174 )
175 .await?;
176 response.json().await.map_err(Into::into)
177 }
178}
179
180#[async_trait]
181impl EmailSender for EmailDelivery {
182 async fn send(&self, email: Email) -> Result<SubmitEmailResponse> {
183 self.send_impl(email).await
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::EmailDelivery;
190 use crate::client::{AuthMode, Oci};
191
192 fn instance_principal_client(region: &str, realm_domain: &str) -> Oci {
193 Oci::builder()
194 .auth_mode(AuthMode::InstancePrincipal)
195 .tenancy_id("ocid1.tenancy.oc1..example")
196 .region(region)
197 .realm_domain_component(realm_domain)
198 .build()
199 .unwrap()
200 }
201
202 #[test]
203 fn email_control_host_uses_commercial_realm_for_instance_principal() {
204 let oci = instance_principal_client("ap-chuncheon-1", "oraclecloud.com");
205 assert_eq!(
206 EmailDelivery::control_host_for(oci.region(), oci.realm_domain()),
207 "ctrl.email.ap-chuncheon-1.oci.oraclecloud.com"
208 );
209 }
210
211 #[test]
212 fn email_control_host_uses_gov_realm_for_instance_principal() {
213 let oci = instance_principal_client("us-langley-1", "oraclegovcloud.com");
214 assert_eq!(
215 EmailDelivery::control_host_for(oci.region(), oci.realm_domain()),
216 "ctrl.email.us-langley-1.oci.oraclegovcloud.com"
217 );
218 }
219}