///|
/// A Hook describes one webhook destination and its delivery policy.
pub(all) struct Hook {
id : String
url : String
secret : String
headers : Array[(String, String)]
active : Bool
max_attempts : Int
retry_policy : RetryPolicy
timeout_ms : Int
} derive(Eq, @debug.Debug)
///|
/// Creates a Hook with safe defaults: active, three attempts, fixed 100ms
/// retry delay, and a 5 second timeout.
pub fn Hook::new(id : String, url : String) -> Hook {
{
id,
url,
secret: "",
headers: [],
active: true,
max_attempts: 3,
retry_policy: Fixed(100),
timeout_ms: 5000,
}
}
///|
pub fn Hook::with_secret(self : Hook, secret : String) -> Hook {
{
id: self.id,
url: self.url,
secret,
headers: self.headers,
active: self.active,
max_attempts: self.max_attempts,
retry_policy: self.retry_policy,
timeout_ms: self.timeout_ms,
}
}
///|
pub fn Hook::with_header(self : Hook, name : String, value : String) -> Hook {
let headers = self.headers.copy()
headers.push((name, value))
{
id: self.id,
url: self.url,
secret: self.secret,
headers,
active: self.active,
max_attempts: self.max_attempts,
retry_policy: self.retry_policy,
timeout_ms: self.timeout_ms,
}
}
///|
pub fn Hook::with_active(self : Hook, active : Bool) -> Hook {
{
id: self.id,
url: self.url,
secret: self.secret,
headers: self.headers,
active,
max_attempts: self.max_attempts,
retry_policy: self.retry_policy,
timeout_ms: self.timeout_ms,
}
}
///|
pub fn Hook::with_max_attempts(self : Hook, max_attempts : Int) -> Hook {
{
id: self.id,
url: self.url,
secret: self.secret,
headers: self.headers,
active: self.active,
max_attempts,
retry_policy: self.retry_policy,
timeout_ms: self.timeout_ms,
}
}
///|
pub fn Hook::with_retry_policy(self : Hook, retry_policy : RetryPolicy) -> Hook {
{
id: self.id,
url: self.url,
secret: self.secret,
headers: self.headers,
active: self.active,
max_attempts: self.max_attempts,
retry_policy,
timeout_ms: self.timeout_ms,
}
}
///|
pub fn Hook::with_timeout_ms(self : Hook, timeout_ms : Int) -> Hook {
{
id: self.id,
url: self.url,
secret: self.secret,
headers: self.headers,
active: self.active,
max_attempts: self.max_attempts,
retry_policy: self.retry_policy,
timeout_ms,
}
}
///|
pub fn Hook::validate(self : Hook) -> Result[Unit, String] {
if self.id == "" {
Err("hook id is required")
} else if self.url == "" {
Err("hook url is required")
} else if self.max_attempts < 1 {
Err("max attempts must be at least 1")
} else if self.timeout_ms <= 0 {
Err("timeout must be positive")
} else if self.retry_policy.validate() is Err(message) {
Err(message)
} else {
Ok(())
}
}
///|
/// Looks up a header value by name, returning the last match. Header names are
/// matched case-insensitively, following HTTP semantics.
pub fn Hook::header_value(self : Hook, name : String) -> String? {
let mut found : String? = None
for item in self.headers {
if header_name_matches(item.0, name) {
found = Some(item.1)
}
}
found
}