Skip to main content

wekan_cli/client/integrations/
inbound_email.rs

1//! Native incoming-email hooks use provider signatures rather than a Wekan session.
2use crate::client::{ClientError, WekanClient, transport::decode_success};
3use reqwest::header::{ACCEPT, CONTENT_TYPE, HeaderMap};
4use serde::{Deserialize, Deserializer, Serialize};
5
6#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
7#[serde(deny_unknown_fields)]
8pub struct InboundEmailReceipt {
9    #[serde(
10        rename(deserialize = "_id", serialize = "resource_id"),
11        deserialize_with = "nullable_id"
12    )]
13    pub resource_id: Option<String>,
14}
15fn nullable_id<'de, D: Deserializer<'de>>(d: D) -> Result<Option<String>, D::Error> {
16    Option::deserialize(d)
17}
18#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
19#[serde(deny_unknown_fields)]
20pub struct BounceReceipt {
21    pub ok: bool,
22    pub recorded: u64,
23}
24impl WekanClient {
25    async fn email_hook(
26        &self,
27        provider: Option<&str>,
28        body: Vec<u8>,
29        content_type: &str,
30        headers: HeaderMap,
31    ) -> Result<Vec<u8>, ClientError> {
32        let mut url = self
33            .server()
34            .join("api/inbound-email")
35            .map_err(|_| super::protocol_error("could not build inbound email endpoint", true))?;
36        if let Some(provider) = provider {
37            url.path_segments_mut()
38                .map_err(|_| super::protocol_error("could not build bounce endpoint", true))?
39                .extend(["bounces", provider]);
40        }
41        self.execute_success_request(
42            self.http
43                .post(url)
44                .headers(headers)
45                .header(ACCEPT, "application/json")
46                .header(CONTENT_TYPE, content_type)
47                .body(body),
48        )
49        .await
50    }
51    pub async fn submit_inbound_email(
52        &self,
53        body: Vec<u8>,
54        content_type: &str,
55        headers: HeaderMap,
56    ) -> Result<InboundEmailReceipt, ClientError> {
57        decode_success(
58            &self.email_hook(None, body, content_type, headers).await?,
59            "inbound email",
60            true,
61        )
62    }
63    pub async fn submit_email_bounce(
64        &self,
65        provider: &str,
66        body: Vec<u8>,
67        content_type: &str,
68        headers: HeaderMap,
69    ) -> Result<BounceReceipt, ClientError> {
70        let response: BounceReceipt = decode_success(
71            &self
72                .email_hook(Some(provider), body, content_type, headers)
73                .await?,
74            "email bounce",
75            true,
76        )?;
77        if !response.ok {
78            return Err(super::protocol_error(
79                "email bounce did not return ok: true",
80                true,
81            ));
82        }
83        Ok(response)
84    }
85}