///|
/// Converts a `WebhookEvent` to a `Json` value.
pub fn event_to_json(event : WebhookEvent) -> Json {
Json::object({
"id": Json::string(event.id),
"event_type": Json::string(event.event_type),
"subject": Json::string(event.subject),
"payload": Json::string(event.payload),
"timestamp_ms": Json::number(event.timestamp_ms.to_double()),
"idempotency_key": Json::string(event.idempotency_key),
})
}
///|
pub fn event_to_json_string(event : WebhookEvent) -> String {
event_to_json(event).stringify()
}
///|
fn json_string_field(map : Map[String, Json], key : String) -> String? {
match map.get(key) {
Some(String(value)) => Some(value)
_ => None
}
}
///|
fn json_timestamp_field(map : Map[String, Json], key : String) -> Int? {
match map.get(key) {
Some(Number(value, ..)) => Some(value.to_int())
_ => None
}
}
///|
pub fn event_from_json(json : Json) -> Result[WebhookEvent, String] {
match json {
Object(map) =>
match
(
json_string_field(map, "id"),
json_string_field(map, "event_type"),
json_string_field(map, "payload"),
json_timestamp_field(map, "timestamp_ms"),
) {
(Some(id), Some(event_type), Some(payload), Some(timestamp_ms)) => {
let subject = match json_string_field(map, "subject") {
Some(value) => value
None => ""
}
let idempotency_key = match
json_string_field(map, "idempotency_key") {
Some(value) => value
None => id + ":" + event_type
}
Ok(
WebhookEvent::new(
id, event_type, subject, payload, timestamp_ms, idempotency_key,
),
)
}
_ => Err("invalid event json")
}
_ => Err("expected json object")
}
}
///|
pub fn event_from_json_string(input : String) -> Result[WebhookEvent, String] {
let json = @json.parse(input) catch { _ => return Err("invalid json") }
event_from_json(json)
}
///|
fn json_int_field(map : Map[String, Json], key : String) -> Int? {
match map.get(key) {
Some(Number(value, ..)) => Some(value.to_int())
_ => None
}
}
///|
fn json_double_field(map : Map[String, Json], key : String) -> Double? {
match map.get(key) {
Some(Number(value, ..)) => Some(value)
_ => None
}
}
///|
fn json_bool_field(map : Map[String, Json], key : String) -> Bool? {
match map.get(key) {
Some(True) => Some(true)
Some(False) => Some(false)
_ => None
}
}
///|
fn json_headers_field(
map : Map[String, Json],
key : String,
) -> Array[(String, String)]? {
match map.get(key) {
Some(Array(items)) => {
let headers : Array[(String, String)] = []
for item in items {
match item {
Object(fields) =>
match
(
json_string_field(fields, "name"),
json_string_field(fields, "value"),
) {
(Some(name), Some(value)) => headers.push((name, value))
_ => return None
}
_ => return None
}
}
Some(headers)
}
_ => None
}
}
///|
/// Converts a `RetryPolicy` to a `Json` value.
pub fn retry_policy_to_json(policy : RetryPolicy) -> Json {
match policy {
None => Json::object({ "kind": Json::string("none") })
Fixed(delay_ms) =>
Json::object({
"kind": Json::string("fixed"),
"delay_ms": Json::number(delay_ms.to_double()),
})
Exponential(initial_ms, multiplier, max_ms) =>
Json::object({
"kind": Json::string("exponential"),
"initial_ms": Json::number(initial_ms.to_double()),
"multiplier": Json::number(multiplier),
"max_ms": Json::number(max_ms.to_double()),
})
}
}
///|
pub fn retry_policy_from_json(json : Json) -> Result[RetryPolicy, String] {
match json {
Object(map) =>
match json_string_field(map, "kind") {
Some("none") => Ok(None)
Some("fixed") =>
match json_int_field(map, "delay_ms") {
Some(delay_ms) => Ok(Fixed(delay_ms))
None => Err("invalid retry policy json")
}
Some("exponential") =>
match
(
json_int_field(map, "initial_ms"),
json_double_field(map, "multiplier"),
json_int_field(map, "max_ms"),
) {
(Some(initial_ms), Some(multiplier), Some(max_ms)) =>
Ok(Exponential(initial_ms, multiplier, max_ms))
_ => Err("invalid retry policy json")
}
_ => Err("invalid retry policy json")
}
_ => Err("invalid retry policy json")
}
}
///|
/// Converts a `Hook` to a `Json` value.
pub fn hook_to_json(hook : Hook) -> Json {
let header_json = hook.headers.map(fn(item) {
Json::object({ "name": Json::string(item.0), "value": Json::string(item.1) })
})
Json::object({
"id": Json::string(hook.id),
"url": Json::string(hook.url),
"secret": Json::string(hook.secret),
"headers": Json::array(header_json),
"active": Json::boolean(hook.active),
"max_attempts": Json::number(hook.max_attempts.to_double()),
"retry_policy": retry_policy_to_json(hook.retry_policy),
"timeout_ms": Json::number(hook.timeout_ms.to_double()),
})
}
///|
pub fn hook_from_json(json : Json) -> Result[Hook, String] {
match json {
Object(map) =>
match
(
json_string_field(map, "id"),
json_string_field(map, "url"),
json_string_field(map, "secret"),
json_headers_field(map, "headers"),
json_bool_field(map, "active"),
json_int_field(map, "max_attempts"),
json_int_field(map, "timeout_ms"),
) {
(
Some(id),
Some(url),
Some(secret),
Some(headers),
Some(active),
Some(max_attempts),
Some(timeout_ms),
) => {
let retry_policy = match map.get("retry_policy") {
Some(policy_json) =>
match retry_policy_from_json(policy_json) {
Ok(policy) => policy
Err(message) => return Err(message)
}
None => Fixed(100)
}
Ok({
id,
url,
secret,
headers,
active,
max_attempts,
retry_policy,
timeout_ms,
})
}
_ => Err("invalid hook json")
}
_ => Err("expected json object")
}
}
///|
pub fn hook_to_json_string(hook : Hook) -> String {
hook_to_json(hook).stringify()
}
///|
pub fn hook_from_json_string(input : String) -> Result[Hook, String] {
let json = @json.parse(input) catch { _ => return Err("invalid json") }
hook_from_json(json)
}
///|
/// Converts a `PendingDelivery` to a `Json` value.
pub fn pending_delivery_to_json(item : PendingDelivery) -> Json {
Json::object({
"hook_id": Json::string(item.hook_id),
"event": event_to_json(item.event),
"queued_at_ms": Json::number(item.queued_at_ms.to_double()),
"attempts_used": Json::number(item.attempts_used.to_double()),
})
}
///|
pub fn pending_delivery_from_json(
json : Json,
) -> Result[PendingDelivery, String] {
match json {
Object(map) =>
match
(
json_string_field(map, "hook_id"),
json_int_field(map, "queued_at_ms"),
json_int_field(map, "attempts_used"),
) {
(Some(hook_id), Some(queued_at_ms), Some(attempts_used)) =>
match map.get("event") {
Some(event_json) =>
match event_from_json(event_json) {
Ok(event) =>
Ok({ hook_id, event, queued_at_ms, attempts_used, })
Err(_) => Err("invalid pending delivery json")
}
None => Err("invalid pending delivery json")
}
_ => Err("invalid pending delivery json")
}
_ => Err("expected json object")
}
}
///|
pub fn pending_delivery_to_json_string(item : PendingDelivery) -> String {
pending_delivery_to_json(item).stringify()
}
///|
pub fn pending_delivery_from_json_string(
input : String,
) -> Result[PendingDelivery, String] {
let json = @json.parse(input) catch { _ => return Err("invalid json") }
pending_delivery_from_json(json)
}
///|
/// Converts a `DeadLetterEntry` to a `Json` value.
pub fn dead_letter_to_json(entry : DeadLetterEntry) -> Json {
Json::object({
"hook_id": Json::string(entry.hook_id),
"event": event_to_json(entry.event),
"reason": Json::string(entry.reason),
"failed_at_ms": Json::number(entry.failed_at_ms.to_double()),
"attempts": Json::number(entry.attempts.to_double()),
})
}
///|
pub fn dead_letter_from_json(json : Json) -> Result[DeadLetterEntry, String] {
match json {
Object(map) =>
match
(
json_string_field(map, "hook_id"),
json_string_field(map, "reason"),
json_int_field(map, "failed_at_ms"),
json_int_field(map, "attempts"),
) {
(Some(hook_id), Some(reason), Some(failed_at_ms), Some(attempts)) =>
match map.get("event") {
Some(event_json) =>
match event_from_json(event_json) {
Ok(event) =>
Ok({ hook_id, event, reason, failed_at_ms, attempts, })
Err(_) => Err("invalid dead letter json")
}
None => Err("invalid dead letter json")
}
_ => Err("invalid dead letter json")
}
_ => Err("expected json object")
}
}
///|
pub fn dead_letter_to_json_string(entry : DeadLetterEntry) -> String {
dead_letter_to_json(entry).stringify()
}
///|
pub fn dead_letter_from_json_string(
input : String,
) -> Result[DeadLetterEntry, String] {
let json = @json.parse(input) catch { _ => return Err("invalid json") }
dead_letter_from_json(json)
}