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 =
159 Self::control_host_for(self.oci_client.region(), self.oci_client.realm_domain());
160 let response = self
161 .oci_client
162 .executor()
163 .execute(
164 Method::GET,
165 RequestTarget {
166 scheme: "https",
167 host: &host,
168 path: &path,
169 },
170 RequestPayload {
171 body: None,
172 content_type: None,
173 extra_headers: Vec::new(),
174 },
175 )
176 .await?;
177 response.json().await.map_err(Into::into)
178 }
179}
180
181#[async_trait]
182impl EmailSender for EmailDelivery {
183 async fn send(&self, email: Email) -> Result<SubmitEmailResponse> {
184 self.send_impl(email).await
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::EmailDelivery;
191 use crate::client::{AuthMode, Oci};
192
193 fn instance_principal_client(region: &str, realm_domain: &str) -> Oci {
194 Oci::builder()
195 .auth_mode(AuthMode::InstancePrincipal)
196 .tenancy_id("ocid1.tenancy.oc1..example")
197 .region(region)
198 .realm_domain_component(realm_domain)
199 .build()
200 .unwrap()
201 }
202
203 #[test]
204 fn email_control_host_uses_commercial_realm_for_instance_principal() {
205 let oci = instance_principal_client("ap-chuncheon-1", "oraclecloud.com");
206 assert_eq!(
207 EmailDelivery::control_host_for(oci.region(), oci.realm_domain()),
208 "ctrl.email.ap-chuncheon-1.oci.oraclecloud.com"
209 );
210 }
211
212 #[test]
213 fn email_control_host_uses_gov_realm_for_instance_principal() {
214 let oci = instance_principal_client("us-langley-1", "oraclegovcloud.com");
215 assert_eq!(
216 EmailDelivery::control_host_for(oci.region(), oci.realm_domain()),
217 "ctrl.email.us-langley-1.oci.oraclegovcloud.com"
218 );
219 }
220}