///|
/// Result of parsing a WebHook JSON payload.
pub(all) struct PayloadDocument {
body : String
json : Json?
error : String
} derive(Debug)
///|
/// Parse JSON while preserving invalid input as a diagnostic value.
pub fn parse_payload(body : String) -> PayloadDocument {
{ body, json: Some(@json.parse(body)), error: "" } catch {
error => { body, json: None, error: error.to_string() }
}
}
///|
/// Return a nested JSON value using object-key path segments.
pub fn json_path(json : Json, path : Array[String]) -> Json? {
let mut current = json
for segment in path {
match current {
Object(fields) =>
match fields.get(segment) {
Some(next) => current = next
None => return None
}
_ => return None
}
}
Some(current)
}
///|
/// Convert JSON values to stable text for normalized event attributes.
fn json_text(value : Json) -> String {
match value {
String(text) => text
True => "true"
False => "false"
Null => "null"
Number(_, ..) | Array(_) | Object(_) => value.stringify()
}
}
///|
/// Read a nested JSON value as text.
pub fn payload_path(body : String, path : Array[String]) -> String? {
match parse_payload(body).json {
Some(json) => json_path(json, path).map(json_text)
None => None
}
}
///|
/// Copy the first existing path into a normalized event attribute.
fn attach_first_path(
event : HookEvent,
key : String,
paths : Array[Array[String]],
) -> HookEvent {
for path in paths {
match payload_path(event.body, path) {
Some(value) => return event.with_attribute(key, value)
None => ()
}
}
event
}
///|
fn first_payload_path(body : String, paths : Array[Array[String]]) -> String? {
for path in paths {
match payload_path(body, path) {
Some(value) => return Some(value)
None => ()
}
}
None
}
///|
fn payload_event_type(request : HookRequest) -> String {
if request.event_type != "unknown" && request.event_type != "" {
return request.event_type
}
let parsed = match request.provider {
Feishu =>
first_payload_path(request.body, [["header", "event_type"], ["type"]])
WeCom => first_payload_path(request.body, [["Event"], ["event"]])
Stripe => first_payload_path(request.body, [["type"]])
GitLab => first_payload_path(request.body, [["object_kind"]])
GitHub | Gitee | Custom(_) => None
}
parsed.unwrap_or("unknown")
}
///|
fn payload_delivery_id(request : HookRequest) -> String {
if request.delivery_id != "unknown" && request.delivery_id != "" {
return request.delivery_id
}
let parsed = match request.provider {
Feishu =>
first_payload_path(request.body, [["header", "event_id"], ["event_id"]])
WeCom => first_payload_path(request.body, [["MsgId"], ["msgid"]])
Stripe => first_payload_path(request.body, [["id"]])
GitHub | GitLab | Gitee | Custom(_) => None
}
parsed.unwrap_or("unknown")
}
///|
/// Enrich a normalized event with real provider payload metadata.
pub fn enrich_provider_event(request : HookRequest) -> HookEvent {
let document = parse_payload(request.body)
let mut event = request
.to_event()
.with_identity(
event_type=payload_event_type(request),
delivery_id=payload_delivery_id(request),
)
.with_attribute(
"payload.valid_json",
if document.json is Some(_) {
"true"
} else {
"false"
},
)
event = match request.provider {
GitHub => {
let enriched = attach_first_path(event, "action", [["action"]])
let enriched = attach_first_path(enriched, "ref", [["ref"]])
let enriched = attach_first_path(enriched, "repository", [
["repository", "full_name"],
["repository", "name"],
])
attach_first_path(enriched, "actor", [["sender", "login"]])
}
GitLab => {
let enriched = attach_first_path(event, "object_kind", [["object_kind"]])
let enriched = attach_first_path(enriched, "ref", [["ref"]])
let enriched = attach_first_path(enriched, "repository", [
["project", "path_with_namespace"],
["repository", "name"],
])
attach_first_path(enriched, "actor", [["user_username"], ["user_name"]])
}
Gitee => {
let enriched = attach_first_path(event, "action", [["action"]])
let enriched = attach_first_path(enriched, "ref", [["ref"]])
let enriched = attach_first_path(enriched, "repository", [
["repository", "full_name"],
["project", "path_with_namespace"],
])
attach_first_path(enriched, "actor", [["sender", "login"], ["user_name"]])
}
Feishu => {
let enriched = attach_first_path(event, "event_id", [
["header", "event_id"],
["event_id"],
])
let enriched = attach_first_path(enriched, "event_type", [
["header", "event_type"],
["type"],
])
attach_first_path(enriched, "actor", [
["event", "sender", "sender_id", "open_id"],
])
}
WeCom => {
let enriched = attach_first_path(event, "message_id", [
["MsgId"],
["msgid"],
])
let enriched = attach_first_path(enriched, "event", [["Event"], ["event"]])
attach_first_path(enriched, "actor", [
["FromUserName"],
["from_user_name"],
])
}
Stripe => {
let enriched = attach_first_path(event, "event_id", [["id"]])
let enriched = attach_first_path(enriched, "event_type", [["type"]])
attach_first_path(enriched, "object_id", [["data", "object", "id"]])
}
Custom(_) => event
}
event
}