KeiSeiKit-1.0/_primitives/_rust/kei-notify-slack/src/payload.rs
Parfii-bot a4e667de10 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

111 lines
3.2 KiB
Rust

// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 <author org>
//
//! Slack incoming-webhook payload builder.
//!
//! Pure function — takes a [`Notification`] and emits a `serde_json::Value`
//! shaped for the Slack `chat.postMessage`-compatible incoming-webhook
//! contract:
//!
//! ```json
//! {
//! "text": "<subject>",
//! "attachments": [
//! { "color": "good|warning|danger|#3b82f6", "title": "...", "text": "..." }
//! ]
//! }
//! ```
//!
//! Colour mapping (Slack conventions + one CSS hex for Info):
//! - `Info` → `#3b82f6` (blue)
//! - `Success` → `good` (green)
//! - `Warn` → `warning` (yellow)
//! - `Error` → `danger` (red)
use kei_runtime_core::traits::notify::{Notification, NotifySeverity};
use serde_json::{json, Value};
/// Map [`NotifySeverity`] to the Slack attachment `color` value.
pub fn severity_color(s: NotifySeverity) -> &'static str {
match s {
NotifySeverity::Info => "#3b82f6",
NotifySeverity::Success => "good",
NotifySeverity::Warn => "warning",
NotifySeverity::Error => "danger",
}
}
/// Build the JSON body for a Slack incoming-webhook POST.
///
/// `text` is set to the subject so Slack's notification preview is sane;
/// the full subject + body lives in the single attachment so severity
/// colouring renders.
pub fn build_payload(n: &Notification) -> Value {
json!({
"text": n.subject,
"attachments": [
{
"color": severity_color(n.severity),
"title": n.subject,
"text": n.body_text,
}
]
})
}
#[cfg(test)]
mod tests {
use super::*;
use kei_runtime_core::DnaBuilder;
fn sample_notification(severity: NotifySeverity) -> Notification {
let dna = DnaBuilder::new("notification")
.cap("NT")
.scope("test")
.body(b"sample")
.build()
.unwrap();
let parent = DnaBuilder::new("primitive")
.cap("PR")
.scope("test-parent")
.body(b"parent")
.build()
.unwrap();
Notification {
dna,
parent_dna: parent,
subject: "subj".into(),
body_text: "body".into(),
body_html: None,
severity,
tags: Vec::new(),
}
}
#[test]
fn info_color_is_blue_hex() {
let n = sample_notification(NotifySeverity::Info);
let v = build_payload(&n);
assert_eq!(v["attachments"][0]["color"], "#3b82f6");
}
#[test]
fn error_color_is_danger() {
let n = sample_notification(NotifySeverity::Error);
let v = build_payload(&n);
assert_eq!(v["attachments"][0]["color"], "danger");
}
#[test]
fn attachment_shape_has_title_and_text() {
let n = sample_notification(NotifySeverity::Warn);
let v = build_payload(&n);
assert_eq!(v["text"], "subj");
let att = &v["attachments"][0];
assert_eq!(att["color"], "warning");
assert_eq!(att["title"], "subj");
assert_eq!(att["text"], "body");
// Must be a single-element array (one severity-coloured block).
assert_eq!(v["attachments"].as_array().unwrap().len(), 1);
}
}