formalize tenant/campaign components of default queue naming

This commit is contained in:
Wez Furlong
2023-02-18 07:28:03 -07:00
parent acf36fe010
commit 667bb91525
3 changed files with 44 additions and 6 deletions
+17
View File
@@ -1,5 +1,22 @@
# kumomta
## Concepts
### Queuing
Messages are assigned to a queue upon reception. The system can support
arbitrarily named queues but the convention is to construct the queue name from
some metadata associated with the message:
* `tenant` - some kind of sender or customer identifier or identity derived
either from the message itself or authentication of the injection session.
* `campaign` - a sender-provided label that can be used to logically group a set
of related messages, perhaps generated from the same campaign.
* destination domain - the site where the email will be routed
These three pieces of information are combined to produce the name of the queue
in the form `campaign:tenant@domain`.
## Debugging/Tracing
This will launch the server using the policy defined in [simple_policy.lua](simple_policy.lua):
+13 -6
View File
@@ -415,12 +415,19 @@ impl SmtpServer {
ids.push(message.id().to_string());
let queue_name = match message.get_meta("queue")? {
serde_json::Value::String(name) => name.to_string(),
serde_json::Value::Null => message.recipient()?.domain().to_string(),
value => anyhow::bail!(
"expected 'queue' metadata to be a string value, got {value:?}"
),
let queue_name = match message.get_meta_string("queue")? {
Some(name) => name.to_string(),
None => {
let campaign = message.get_meta_string("campaign")?;
let tenant = message.get_meta_string("tenant")?;
let domain = message.recipient()?.domain().to_string();
match (campaign, tenant) {
(Some(c), Some(t)) => format!("{c}:{t}@{domain}"),
(Some(c), None) => format!("{c}:@{domain}"),
(None, Some(t)) => format!("{t}@{domain}"),
(None, None) => domain,
}
}
};
if queue_name != "null" {
+14
View File
@@ -227,6 +227,20 @@ impl Message {
}
}
/// Retrieve `key` as a String.
pub fn get_meta_string<S: serde_json::value::Index + std::fmt::Display + Copy>(
&self,
key: S,
) -> anyhow::Result<Option<String>> {
match self.get_meta(key) {
Ok(serde_json::Value::String(value)) => Ok(Some(value.to_string())),
Ok(serde_json::Value::Null) => Ok(None),
hmm => {
anyhow::bail!("expected '{key}' to be a string value, got {hmm:?}");
}
}
}
pub fn get_meta<S: serde_json::value::Index>(
&self,
key: S,