feat(email): integrate email service using AWS SES (#1158)

This commit is contained in:
Chethan Rao
2023-05-17 15:12:32 +05:30
committed by GitHub
parent ea98145318
commit 07e0fcbe06
9 changed files with 226 additions and 33 deletions

92
Cargo.lock generated
View File

@ -598,9 +598,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-credential-types" name = "aws-credential-types"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4232d3729eefc287adc0d5a8adc97b7d94eefffe6bbe94312cc86c7ab6b06ce" checksum = "4cb57ac6088805821f78d282c0ba8aec809f11cbee10dda19a97b03ab040ccc2"
dependencies = [ dependencies = [
"aws-smithy-async", "aws-smithy-async",
"aws-smithy-types", "aws-smithy-types",
@ -612,9 +612,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-endpoint" name = "aws-endpoint"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f04ab03b3f1cca91f7cccaa213056d732accb14e2e65debfacc1d28627d162" checksum = "9c5f6f84a4f46f95a9bb71d9300b73cd67eb868bc43ae84f66ad34752299f4ac"
dependencies = [ dependencies = [
"aws-smithy-http", "aws-smithy-http",
"aws-smithy-types", "aws-smithy-types",
@ -626,9 +626,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-http" name = "aws-http"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ad8c53f7560baaf635b6aa811f3213d39b50555d100f83e43801652d4e318e" checksum = "a754683c322f7dc5167484266489fdebdcd04d26e53c162cad1f3f949f2c5671"
dependencies = [ dependencies = [
"aws-credential-types", "aws-credential-types",
"aws-smithy-http", "aws-smithy-http",
@ -701,6 +701,31 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "aws-sdk-sesv2"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28ec96086c4bda28c512b10c5c951d031651be454a512e511cf5fe91b21b9cc9"
dependencies = [
"aws-credential-types",
"aws-endpoint",
"aws-http",
"aws-sig-auth",
"aws-smithy-async",
"aws-smithy-client",
"aws-smithy-http",
"aws-smithy-http-tower",
"aws-smithy-json",
"aws-smithy-types",
"aws-types",
"bytes",
"http",
"regex",
"tokio-stream",
"tower",
"tracing",
]
[[package]] [[package]]
name = "aws-sdk-sso" name = "aws-sdk-sso"
version = "0.26.0" version = "0.26.0"
@ -754,9 +779,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-sig-auth" name = "aws-sig-auth"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d77d879ab210e958ba65a6d3842969a596738c024989cd3e490cf9f9b560ec" checksum = "84dc92a63ede3c2cbe43529cb87ffa58763520c96c6a46ca1ced80417afba845"
dependencies = [ dependencies = [
"aws-credential-types", "aws-credential-types",
"aws-sigv4", "aws-sigv4",
@ -769,9 +794,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-sigv4" name = "aws-sigv4"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ab4eebc8ec484fb9eab04b15a5d1e71f3dc13bee8fdd2d9ed78bcd6ecbd7192" checksum = "392fefab9d6fcbd76d518eb3b1c040b84728ab50f58df0c3c53ada4bea9d327e"
dependencies = [ dependencies = [
"aws-smithy-eventstream", "aws-smithy-eventstream",
"aws-smithy-http", "aws-smithy-http",
@ -790,9 +815,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-async" name = "aws-smithy-async"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88573bcfbe1dcfd54d4912846df028b42d6255cbf9ce07be216b1bbfd11fc4b9" checksum = "ae23b9fe7a07d0919000116c4c5c0578303fbce6fc8d32efca1f7759d4c20faf"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"pin-project-lite", "pin-project-lite",
@ -823,9 +848,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-client" name = "aws-smithy-client"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2f52352bae50d3337d5d6151b695d31a8c10ebea113eca5bead531f8301b067" checksum = "5230d25d244a51339273b8870f0f77874cd4449fb4f8f629b21188ae10cfc0ba"
dependencies = [ dependencies = [
"aws-smithy-async", "aws-smithy-async",
"aws-smithy-http", "aws-smithy-http",
@ -847,9 +872,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-eventstream" name = "aws-smithy-eventstream"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168f08f8439c8b317b578a695e514c5cd7b869e73849a2d6b71ced4de6ce193d" checksum = "22d2a2bcc16e5c4d949ffd2b851da852b9bbed4bb364ed4ae371b42137ca06d9"
dependencies = [ dependencies = [
"aws-smithy-types", "aws-smithy-types",
"bytes", "bytes",
@ -858,9 +883,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-http" name = "aws-smithy-http"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03bcc02d7ed9649d855c8ce4a735e9848d7b8f7568aad0504c158e3baa955df8" checksum = "b60e2133beb9fe6ffe0b70deca57aaeff0a35ad24a9c6fab2fd3b4f45b99fdb5"
dependencies = [ dependencies = [
"aws-smithy-eventstream", "aws-smithy-eventstream",
"aws-smithy-types", "aws-smithy-types",
@ -881,9 +906,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-http-tower" name = "aws-smithy-http-tower"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da88b3a860f65505996c29192d800f1aeb9480440f56d63aad33a3c12045017a" checksum = "3a4d94f556c86a0dd916a5d7c39747157ea8cb909ca469703e20fee33e448b67"
dependencies = [ dependencies = [
"aws-smithy-http", "aws-smithy-http",
"aws-smithy-types", "aws-smithy-types",
@ -897,9 +922,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-json" name = "aws-smithy-json"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b0c1e87d75cac889dca2a7f5ba280da2cde8122448e7fec1d614194dfa00c70" checksum = "5ce3d6e6ebb00b2cce379f079ad5ec508f9bcc3a9510d9b9c1840ed1d6f8af39"
dependencies = [ dependencies = [
"aws-smithy-types", "aws-smithy-types",
] ]
@ -916,9 +941,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-types" name = "aws-smithy-types"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0afc731fd1417d791f9145a1e0c30e23ae0beaab9b4814017708ead2fc20f1" checksum = "58db46fc1f4f26be01ebdb821751b4e2482cd43aa2b64a0348fb89762defaffa"
dependencies = [ dependencies = [
"base64-simd", "base64-simd",
"itoa", "itoa",
@ -938,9 +963,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-types" name = "aws-types"
version = "0.55.1" version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9b082e329d9a304d39e193ad5c7ab363a0d6507aca6965e0673a746686fb0cc" checksum = "de0869598bfe46ec44ffe17e063ed33336e59df90356ca8ff0e8da6f7c1d994b"
dependencies = [ dependencies = [
"aws-credential-types", "aws-credential-types",
"aws-smithy-async", "aws-smithy-async",
@ -1795,11 +1820,16 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
name = "external_services" name = "external_services"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait",
"aws-config", "aws-config",
"aws-sdk-kms", "aws-sdk-kms",
"aws-sdk-sesv2",
"aws-smithy-client",
"base64 0.21.0", "base64 0.21.0",
"common_utils", "common_utils",
"dyn-clone",
"error-stack", "error-stack",
"masking",
"once_cell", "once_cell",
"router_env", "router_env",
"serde", "serde",
@ -2970,7 +3000,7 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry" name = "opentelemetry"
version = "0.18.0" version = "0.18.0"
source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
dependencies = [ dependencies = [
"opentelemetry_api", "opentelemetry_api",
"opentelemetry_sdk", "opentelemetry_sdk",
@ -2979,7 +3009,7 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-otlp" name = "opentelemetry-otlp"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"futures", "futures",
@ -2996,7 +3026,7 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-proto" name = "opentelemetry-proto"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
dependencies = [ dependencies = [
"futures", "futures",
"futures-util", "futures-util",
@ -3008,7 +3038,7 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry_api" name = "opentelemetry_api"
version = "0.18.0" version = "0.18.0"
source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
dependencies = [ dependencies = [
"fnv", "fnv",
"futures-channel", "futures-channel",
@ -3023,7 +3053,7 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry_sdk" name = "opentelemetry_sdk"
version = "0.18.0" version = "0.18.0"
source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"crossbeam-channel", "crossbeam-channel",

View File

@ -242,6 +242,12 @@ paypal = { currency = "USD,INR", country = "US" }
key_id = "" # The AWS key ID used by the KMS SDK for decrypting data. key_id = "" # The AWS key ID used by the KMS SDK for decrypting data.
region = "" # The AWS region used by the KMS SDK for decrypting data. region = "" # The AWS region used by the KMS SDK for decrypting data.
# EmailClient configuration. Only applicable when the `email` feature flag is enabled.
[email]
from_email = "notify@example.com" # Sender email
aws_region = "" # AWS region used by AWS SES
base_url = "" # Base url used when adding links that should redirect to self
[dummy_connector] [dummy_connector]
payment_ttl = 172800 # Time to live for dummy connector payment in redis payment_ttl = 172800 # Time to live for dummy connector payment in redis
payment_duration = 1000 # Fake delay duration for dummy connector payment payment_duration = 1000 # Fake delay duration for dummy connector payment

View File

@ -149,6 +149,11 @@ stream = "SCHEDULER_STREAM"
disabled = false disabled = false
consumer_group = "SCHEDULER_GROUP" consumer_group = "SCHEDULER_GROUP"
[email]
from_email = "notify@example.com"
aws_region = ""
base_url = ""
[bank_config.eps] [bank_config.eps]
stripe = { banks = "arzte_und_apotheker_bank,austrian_anadi_bank_ag,bank_austria,bankhaus_carl_spangler,bankhaus_schelhammer_und_schattera_ag,bawag_psk_ag,bks_bank_ag,brull_kallmus_bank_ag,btv_vier_lander_bank,capital_bank_grawe_gruppe_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_alpeadriabank_international_ag,hypo_noe_lb_fur_niederosterreich_u_wien,hypo_oberosterreich_salzburg_steiermark,hypo_tirol_bank_ag,hypo_vorarlberg_bank_ag,hypo_bank_burgenland_aktiengesellschaft,marchfelder_bank,oberbank_ag,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag,vr_bank_braunau" } stripe = { banks = "arzte_und_apotheker_bank,austrian_anadi_bank_ag,bank_austria,bankhaus_carl_spangler,bankhaus_schelhammer_und_schattera_ag,bawag_psk_ag,bks_bank_ag,brull_kallmus_bank_ag,btv_vier_lander_bank,capital_bank_grawe_gruppe_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_alpeadriabank_international_ag,hypo_noe_lb_fur_niederosterreich_u_wien,hypo_oberosterreich_salzburg_steiermark,hypo_tirol_bank_ag,hypo_vorarlberg_bank_ag,hypo_bank_burgenland_aktiengesellschaft,marchfelder_bank,oberbank_ag,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag,vr_bank_braunau" }
adyen = { banks = "bank_austria,bawag_psk_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_tirol_bank_ag,posojilnica_bank_e_gen,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag" } adyen = { banks = "bank_austria,bawag_psk_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_tirol_bank_ag,posojilnica_bank_e_gen,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag" }

View File

@ -9,6 +9,7 @@ license = "Apache-2.0"
[features] [features]
kms = ["dep:aws-config", "dep:aws-sdk-kms"] kms = ["dep:aws-config", "dep:aws-sdk-kms"]
email = ["dep:aws-config"]
[dependencies] [dependencies]
aws-config = { version = "0.55.1", optional = true } aws-config = { version = "0.55.1", optional = true }
@ -19,7 +20,12 @@ once_cell = "1.17.1"
serde = { version = "1.0.160", features = ["derive"] } serde = { version = "1.0.160", features = ["derive"] }
thiserror = "1.0.40" thiserror = "1.0.40"
tokio = "1.27.0" tokio = "1.27.0"
dyn-clone = "1.0.11"
async-trait = "0.1.66"
aws-sdk-sesv2 = "0.27.0"
aws-smithy-client = "0.55.0"
# First party crates # First party crates
common_utils = { version = "0.1.0", path = "../common_utils" } common_utils = { version = "0.1.0", path = "../common_utils" }
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
masking = { version = "0.1.0", path = "../masking" }

View File

@ -0,0 +1,123 @@
//! Interactions with the AWS SES SDK
use aws_config::meta::region::RegionProviderChain;
use aws_sdk_sesv2::{
config::Region,
operation::send_email::SendEmailError,
types::{Body, Content, Destination, EmailContent, Message},
Client,
};
use common_utils::{errors::CustomResult, pii};
use error_stack::{IntoReport, ResultExt};
use masking::PeekInterface;
use serde::Deserialize;
/// Custom Result type alias for Email operations.
pub type EmailResult<T> = CustomResult<T, EmailError>;
/// A trait that defines the methods that must be implemented to send email.
#[async_trait::async_trait]
pub trait EmailClient: Sync + Send + dyn_clone::DynClone {
/// Sends an email to the specified recipient with the given subject and body.
async fn send_email(
&self,
recipient: pii::Email,
subject: String,
body: String,
) -> EmailResult<()>;
}
dyn_clone::clone_trait_object!(EmailClient);
/// Struct that contains the settings required to construct an EmailClient.
#[derive(Debug, Clone, Default, Deserialize)]
pub struct EmailSettings {
/// Sender email.
pub from_email: String,
/// The AWS region to send SES requests to.
pub aws_region: String,
/// Base-url used when adding links that should redirect to self
pub base_url: String,
}
/// Client for AWS SES operation
#[derive(Debug, Clone)]
pub struct AwsSes {
ses_client: Client,
from_email: String,
}
impl AwsSes {
/// Constructs a new AwsSes client
pub async fn new(conf: &EmailSettings) -> Self {
let region_provider = RegionProviderChain::first_try(Region::new(conf.aws_region.clone()));
let sdk_config = aws_config::from_env().region(region_provider).load().await;
Self {
ses_client: Client::new(&sdk_config),
from_email: conf.from_email.clone(),
}
}
}
#[async_trait::async_trait]
impl EmailClient for AwsSes {
async fn send_email(
&self,
recipient: pii::Email,
subject: String,
body: String,
) -> EmailResult<()> {
self.ses_client
.send_email()
.from_email_address(self.from_email.to_owned())
.destination(
Destination::builder()
.to_addresses(recipient.peek())
.build(),
)
.content(
EmailContent::builder()
.simple(
Message::builder()
.subject(Content::builder().data(subject).build())
.body(
Body::builder()
.text(Content::builder().data(body).charset("UTF-8").build())
.build(),
)
.build(),
)
.build(),
)
.send()
.await
.map_err(AwsSesError::SendingFailure)
.into_report()
.change_context(EmailError::EmailSendingFailure)?;
Ok(())
}
}
/// Errors that could occur from EmailClient.
#[derive(Debug, thiserror::Error)]
pub enum EmailError {
/// An error occurred when building email client.
#[error("Error building email client")]
ClientBuildingFailure,
/// An error occurred when sending email
#[error("Error sending email to recipient")]
EmailSendingFailure,
}
/// Errors that could occur during SES operations.
#[derive(Debug, thiserror::Error)]
pub enum AwsSesError {
/// An error occurred in the SDK while sending email.
#[error("Failed to Send Email {0:?}")]
SendingFailure(aws_smithy_client::SdkError<SendEmailError>),
}

View File

@ -3,6 +3,9 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations)] #![warn(missing_docs, missing_debug_implementations)]
#[cfg(feature = "email")]
pub mod email;
#[cfg(feature = "kms")] #[cfg(feature = "kms")]
pub mod kms; pub mod kms;

View File

@ -13,10 +13,11 @@ build = "src/build.rs"
default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector"] default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector"]
s3 = ["dep:aws-sdk-s3","dep:aws-config"] s3 = ["dep:aws-sdk-s3","dep:aws-config"]
kms = ["external_services/kms","dep:aws-config"] kms = ["external_services/kms","dep:aws-config"]
email = ["external_services/email","dep:aws-config"]
basilisk = ["kms"] basilisk = ["kms"]
stripe = ["dep:serde_qs"] stripe = ["dep:serde_qs"]
sandbox = ["kms", "stripe", "basilisk", "s3"] sandbox = ["kms", "stripe", "basilisk", "s3", "email"]
production = ["kms", "stripe", "basilisk", "s3"] production = ["kms", "stripe", "basilisk", "s3", "email"]
olap = [] olap = []
oltp = [] oltp = []
kv_store = [] kv_store = []

View File

@ -7,6 +7,8 @@ use std::{
use api_models::enums; use api_models::enums;
use common_utils::ext_traits::ConfigExt; use common_utils::ext_traits::ConfigExt;
use config::{Environment, File}; use config::{Environment, File};
#[cfg(feature = "email")]
use external_services::email::EmailSettings;
#[cfg(feature = "kms")] #[cfg(feature = "kms")]
use external_services::kms; use external_services::kms;
use redis_interface::RedisSettings; use redis_interface::RedisSettings;
@ -69,6 +71,8 @@ pub struct Settings {
pub connector_customer: ConnectorCustomer, pub connector_customer: ConnectorCustomer,
#[cfg(feature = "dummy_connector")] #[cfg(feature = "dummy_connector")]
pub dummy_connector: DummyConnector, pub dummy_connector: DummyConnector,
#[cfg(feature = "email")]
pub email: EmailSettings,
} }
#[derive(Debug, Deserialize, Clone, Default)] #[derive(Debug, Deserialize, Clone, Default)]

View File

@ -1,4 +1,6 @@
use actix_web::{web, Scope}; use actix_web::{web, Scope};
#[cfg(feature = "email")]
use external_services::email::{AwsSes, EmailClient};
use tokio::sync::oneshot; use tokio::sync::oneshot;
#[cfg(feature = "dummy_connector")] #[cfg(feature = "dummy_connector")]
@ -22,12 +24,16 @@ pub struct AppState {
pub flow_name: String, pub flow_name: String,
pub store: Box<dyn StorageInterface>, pub store: Box<dyn StorageInterface>,
pub conf: Settings, pub conf: Settings,
#[cfg(feature = "email")]
pub email_client: Box<dyn EmailClient>,
} }
pub trait AppStateInfo { pub trait AppStateInfo {
fn conf(&self) -> Settings; fn conf(&self) -> Settings;
fn flow_name(&self) -> String; fn flow_name(&self) -> String;
fn store(&self) -> Box<dyn StorageInterface>; fn store(&self) -> Box<dyn StorageInterface>;
#[cfg(feature = "email")]
fn email_client(&self) -> Box<dyn EmailClient>;
} }
impl AppStateInfo for AppState { impl AppStateInfo for AppState {
@ -40,6 +46,10 @@ impl AppStateInfo for AppState {
fn store(&self) -> Box<dyn StorageInterface> { fn store(&self) -> Box<dyn StorageInterface> {
self.store.to_owned() self.store.to_owned()
} }
#[cfg(feature = "email")]
fn email_client(&self) -> Box<dyn EmailClient> {
self.email_client.to_owned()
}
} }
impl AppState { impl AppState {
@ -56,10 +66,15 @@ impl AppState {
StorageImpl::Mock => Box::new(MockDb::new(&conf).await), StorageImpl::Mock => Box::new(MockDb::new(&conf).await),
}; };
#[cfg(feature = "email")]
#[allow(clippy::expect_used)]
let email_client = Box::new(AwsSes::new(&conf.email).await);
Self { Self {
flow_name: String::from("default"), flow_name: String::from("default"),
store, store,
conf, conf,
#[cfg(feature = "email")]
email_client,
} }
} }