azure_communications/
communication.rs

1use crate::utils::{create_authorization_signature, format_utc_string};
2use anyhow::Result;
3use base64::{engine::general_purpose::STANDARD as base64, Engine};
4use regex::Regex;
5use reqwest::{
6    header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE},
7    Client, Url,
8};
9use ring::digest::{digest, SHA256};
10use time::OffsetDateTime;
11
12pub mod email;
13pub mod sms;
14
15#[derive(Debug, Clone)]
16pub struct AzureCommunicationService {
17    pub endpoint: String,
18    pub access_key: String,
19    pub request_client: Client,
20}
21
22impl AzureCommunicationService {
23    pub fn new(connection_string: &str, request_client: Option<Client>) -> Self {
24        let re = Regex::new(r"endpoint=(.*?);accesskey=(.*)").expect("Invalid regex");
25        let caps = re
26            .captures(connection_string)
27            .expect("Invalid connection string");
28
29        let endpoint = caps[1].to_string();
30        let access_key = caps[2].to_string();
31        let request_client = request_client.unwrap_or_default();
32
33        AzureCommunicationService {
34            endpoint: endpoint.to_string(),
35            access_key: access_key.to_string(),
36            request_client,
37        }
38    }
39}
40
41impl AzureCommunicationService {
42    fn create_az_request(&self, url: Url, body: String) -> Result<reqwest::RequestBuilder> {
43        let access_key = &self.access_key;
44        let client = &self.request_client;
45
46        let content_hash = digest(&SHA256, body.as_bytes());
47        let content_hash_base64 = base64.encode(content_hash);
48
49        let now = OffsetDateTime::now_utc();
50        let date = format_utc_string(now)?;
51
52        let signature = create_authorization_signature(&url, &body, access_key, &date)?;
53
54        let mut headers = HeaderMap::new();
55        headers.insert("x-ms-date", HeaderValue::from_str(&date)?);
56        headers.insert(
57            "x-ms-content-sha256",
58            HeaderValue::from_str(&content_hash_base64)?,
59        );
60        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
61        headers.insert(
62            AUTHORIZATION,
63            HeaderValue::from_str(&format!(
64                "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={}",
65                signature
66            ))?,
67        );
68
69        let request = client.post(url).headers(headers).body(body);
70
71        Ok(request)
72    }
73}