use http::StatusCode;
use serde_json::{json, Value};
use fakecloud_core::service::{AwsRequest, AwsResponse, AwsServiceError};
use crate::state::ConfigurationSet;
use crate::state::SesState;
use super::{
event_destination_to_json, extract_string_array, parse_event_destination_definition,
SesV2Service,
};
impl SesV2Service {
pub(super) fn create_configuration_set(
&self,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let name = match body["ConfigurationSetName"].as_str() {
Some(n) => n.to_string(),
None => {
return Ok(Self::json_error(
StatusCode::BAD_REQUEST,
"BadRequestException",
"ConfigurationSetName is required",
));
}
};
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
if state.configuration_sets.contains_key(&name) {
return Ok(Self::json_error(
StatusCode::CONFLICT,
"AlreadyExistsException",
&format!("Configuration set {} already exists", name),
));
}
let sending_enabled = body["SendingOptions"]["SendingEnabled"]
.as_bool()
.unwrap_or(true);
let tls_policy = body["DeliveryOptions"]["TlsPolicy"]
.as_str()
.unwrap_or("OPTIONAL")
.to_string();
let sending_pool_name = body["DeliveryOptions"]["SendingPoolName"]
.as_str()
.map(|s| s.to_string());
let max_delivery_seconds = body["DeliveryOptions"]["MaxDeliverySeconds"].as_i64();
let custom_redirect_domain = body["TrackingOptions"]["CustomRedirectDomain"]
.as_str()
.map(|s| s.to_string());
let https_policy = body["TrackingOptions"]["HttpsPolicy"]
.as_str()
.map(|s| s.to_string());
let suppressed_reasons =
extract_string_array(&body["SuppressionOptions"]["SuppressedReasons"]);
let reputation_metrics_enabled = body["ReputationOptions"]["ReputationMetricsEnabled"]
.as_bool()
.unwrap_or(false);
let vdm_options = if body["VdmOptions"].is_object() {
Some(body["VdmOptions"].clone())
} else {
None
};
let archive_arn = body["ArchivingOptions"]["ArchiveArn"]
.as_str()
.map(|s| s.to_string());
let archiving_options_present = body["ArchivingOptions"].is_object();
state.configuration_sets.insert(
name.clone(),
ConfigurationSet {
name: name.clone(),
sending_enabled,
tls_policy,
sending_pool_name,
max_delivery_seconds,
custom_redirect_domain,
https_policy,
suppressed_reasons,
reputation_metrics_enabled,
vdm_options,
archive_arn,
archiving_options_present,
},
);
let arn = format!(
"arn:aws:ses:{}:{}:configuration-set/{}",
req.region, req.account_id, name
);
if let Some(tags_arr) = body["Tags"].as_array() {
let mut tag_map = std::collections::BTreeMap::new();
for tag in tags_arr {
if let (Some(k), Some(v)) = (tag["Key"].as_str(), tag["Value"].as_str()) {
tag_map.insert(k.to_string(), v.to_string());
}
}
state.tags.insert(arn, tag_map);
} else {
state.tags.remove(&arn);
}
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn list_configuration_sets(
&self,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let accounts = self.state.read();
let empty = SesState::new(&req.account_id, &req.region);
let state = accounts.get(&req.account_id).unwrap_or(&empty);
let sets: Vec<Value> = state
.configuration_sets
.keys()
.map(|name| json!(name))
.collect();
let response = json!({
"ConfigurationSets": sets,
});
Ok(AwsResponse::json(StatusCode::OK, response.to_string()))
}
pub(super) fn get_configuration_set(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let accounts = self.state.read();
let empty = SesState::new(&req.account_id, &req.region);
let state = accounts.get(&req.account_id).unwrap_or(&empty);
let cs = match state.configuration_sets.get(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
let delivery_options = if cs.tls_policy != "OPTIONAL"
|| cs.sending_pool_name.is_some()
|| cs.max_delivery_seconds.is_some()
{
let mut d = json!({ "TlsPolicy": cs.tls_policy });
if let Some(ref pool) = cs.sending_pool_name {
d["SendingPoolName"] = json!(pool);
}
if let Some(secs) = cs.max_delivery_seconds {
d["MaxDeliverySeconds"] = json!(secs);
}
Some(d)
} else {
None
};
let tracking_options = if cs.custom_redirect_domain.is_some() || cs.https_policy.is_some() {
let mut t = json!({});
if let Some(ref domain) = cs.custom_redirect_domain {
t["CustomRedirectDomain"] = json!(domain);
}
if let Some(ref policy) = cs.https_policy {
t["HttpsPolicy"] = json!(policy);
}
Some(t)
} else {
None
};
let mut response = json!({
"ConfigurationSetName": name,
"ReputationOptions": {
"ReputationMetricsEnabled": cs.reputation_metrics_enabled,
},
"SendingOptions": {
"SendingEnabled": cs.sending_enabled,
},
"Tags": [],
});
if let Some(d) = delivery_options {
response["DeliveryOptions"] = d;
}
if let Some(t) = tracking_options {
response["TrackingOptions"] = t;
}
if !cs.suppressed_reasons.is_empty() {
response["SuppressionOptions"] = json!({
"SuppressedReasons": cs.suppressed_reasons,
});
}
if let Some(ref vdm) = cs.vdm_options {
response["VdmOptions"] = vdm.clone();
}
if cs.archiving_options_present || cs.archive_arn.is_some() {
let mut archiving = serde_json::Map::new();
if let Some(ref arn) = cs.archive_arn {
archiving.insert("ArchiveArn".to_string(), json!(arn));
}
response["ArchivingOptions"] = Value::Object(archiving);
}
let arn = format!(
"arn:aws:ses:{}:{}:configuration-set/{}",
req.region, req.account_id, name
);
if let Some(tag_map) = state.tags.get(&arn) {
response["Tags"] =
Value::Array(fakecloud_core::tags::tags_to_json(tag_map, "Key", "Value"));
}
Ok(AwsResponse::json(StatusCode::OK, response.to_string()))
}
pub(super) fn delete_configuration_set(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
if state.configuration_sets.remove(name).is_none() {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
let arn = format!(
"arn:aws:ses:{}:{}:configuration-set/{}",
req.region, req.account_id, name
);
state.tags.remove(&arn);
state.event_destinations.remove(name);
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_sending_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
if let Some(enabled) = body["SendingEnabled"].as_bool() {
cs.sending_enabled = enabled;
}
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_delivery_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
if let Some(policy) = body["TlsPolicy"].as_str() {
cs.tls_policy = policy.to_string();
}
if let Some(pool) = body["SendingPoolName"].as_str() {
cs.sending_pool_name = Some(pool.to_string());
}
if let Some(secs) = body["MaxDeliverySeconds"].as_i64() {
cs.max_delivery_seconds = Some(secs);
}
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_tracking_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
if let Some(domain) = body["CustomRedirectDomain"].as_str() {
cs.custom_redirect_domain = Some(domain.to_string());
}
if let Some(policy) = body["HttpsPolicy"].as_str() {
cs.https_policy = Some(policy.to_string());
}
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_suppression_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
cs.suppressed_reasons = extract_string_array(&body["SuppressedReasons"]);
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_reputation_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
if let Some(enabled) = body["ReputationMetricsEnabled"].as_bool() {
cs.reputation_metrics_enabled = enabled;
}
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_vdm_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
cs.vdm_options = Some(body);
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn put_configuration_set_archiving_options(
&self,
name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let cs = match state.configuration_sets.get_mut(name) {
Some(cs) => cs,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", name),
));
}
};
cs.archive_arn = body["ArchiveArn"].as_str().map(|s| s.to_string());
cs.archiving_options_present = true;
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn create_configuration_set_event_destination(
&self,
config_set_name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let accounts_read = self.state.read();
let empty = SesState::new(&req.account_id, &req.region);
let state_read = accounts_read.get(&req.account_id).unwrap_or(&empty);
if !state_read.configuration_sets.contains_key(config_set_name) {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", config_set_name),
));
}
drop(accounts_read);
let dest_name = match body["EventDestinationName"].as_str() {
Some(n) => n.to_string(),
None => {
return Ok(Self::json_error(
StatusCode::BAD_REQUEST,
"BadRequestException",
"EventDestinationName is required",
));
}
};
let event_dest = parse_event_destination_definition(&dest_name, &body["EventDestination"]);
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
let dests = state
.event_destinations
.entry(config_set_name.to_string())
.or_default();
if dests.iter().any(|d| d.name == dest_name) {
return Ok(Self::json_error(
StatusCode::CONFLICT,
"AlreadyExistsException",
&format!("Event destination {} already exists", dest_name),
));
}
dests.push(event_dest);
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn get_configuration_set_event_destinations(
&self,
config_set_name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let accounts = self.state.read();
let empty = SesState::new(&req.account_id, &req.region);
let state = accounts.get(&req.account_id).unwrap_or(&empty);
if !state.configuration_sets.contains_key(config_set_name) {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", config_set_name),
));
}
let dests = state
.event_destinations
.get(config_set_name)
.cloned()
.unwrap_or_default();
let dests_json: Vec<Value> = dests.iter().map(event_destination_to_json).collect();
let response = json!({
"EventDestinations": dests_json,
});
Ok(AwsResponse::json(StatusCode::OK, response.to_string()))
}
pub(super) fn update_configuration_set_event_destination(
&self,
config_set_name: &str,
dest_name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let body: Value = Self::parse_body(req)?;
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
if !state.configuration_sets.contains_key(config_set_name) {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", config_set_name),
));
}
let dests = state
.event_destinations
.entry(config_set_name.to_string())
.or_default();
let existing = match dests.iter_mut().find(|d| d.name == dest_name) {
Some(d) => d,
None => {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Event destination {} does not exist", dest_name),
));
}
};
let updated = parse_event_destination_definition(dest_name, &body["EventDestination"]);
*existing = updated;
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
pub(super) fn delete_configuration_set_event_destination(
&self,
config_set_name: &str,
dest_name: &str,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let mut accounts = self.state.write();
let state = accounts.get_or_create(&req.account_id);
if !state.configuration_sets.contains_key(config_set_name) {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Configuration set {} does not exist", config_set_name),
));
}
let dests = state
.event_destinations
.entry(config_set_name.to_string())
.or_default();
let len_before = dests.len();
dests.retain(|d| d.name != dest_name);
if dests.len() == len_before {
return Ok(Self::json_error(
StatusCode::NOT_FOUND,
"NotFoundException",
&format!("Event destination {} does not exist", dest_name),
));
}
Ok(AwsResponse::json(StatusCode::OK, "{}"))
}
}