refactor(notify): extract types to separate module and redesign
NotificationRule - Move NotifyEvent, NotifyPluginMap, and related types to notify/types.rs - Introduce NotificationRule struct replacing NotificationMethod fields - Add NotificationRulesMap for priority-based notification configuration - Comment out main event loop and add todo!() placeholders for WIP
This commit is contained in:
@@ -99,18 +99,6 @@ pub struct PluginRuntimeConfig {
|
|||||||
/// Notification method configuration
|
/// Notification method configuration
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct NotificationMethod {
|
pub struct NotificationMethod {
|
||||||
/// Whether notification failure should not trigger next priority group
|
|
||||||
#[serde(default)]
|
|
||||||
pub fallible: bool,
|
|
||||||
|
|
||||||
/// Number of retries on failure (0 = no retry, -1 = infinite retries)
|
|
||||||
#[serde(default)]
|
|
||||||
pub retry: i8,
|
|
||||||
|
|
||||||
/// Timeout for notification execution in milliseconds (0 = no timeout)
|
|
||||||
#[serde(default)]
|
|
||||||
pub timeout_ms: u64,
|
|
||||||
|
|
||||||
/// Template definition
|
/// Template definition
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub template: NotificationTemplate,
|
pub template: NotificationTemplate,
|
||||||
|
|||||||
+55
-109
@@ -1,23 +1,24 @@
|
|||||||
use crate::constant::FINALIZE_STANDALONE_PLUGIN_PATH;
|
use crate::constant::FINALIZE_STANDALONE_PLUGIN_PATH;
|
||||||
use crate::finalize::FinalizeConfig;
|
use crate::finalize::FinalizeConfig;
|
||||||
use crate::finalize::NotificationConfig;
|
|
||||||
use crate::finalize::parse;
|
use crate::finalize::parse;
|
||||||
use crate::finalize::plugin;
|
use crate::finalize::plugin;
|
||||||
use crate::finalize::plugin::FinalizePlugin;
|
|
||||||
use crate::finalize::plugin::fetch;
|
use crate::finalize::plugin::fetch;
|
||||||
use crate::types::buildstatus::StagePhase;
|
use crate::notify::types::BundledNotifyEvent;
|
||||||
use crate::types::buildstatus::StageResult;
|
use crate::notify::types::NOTIFY_TEMPORARY_DELAY_FACTOR;
|
||||||
|
use crate::notify::types::NOTIFY_TEMPORARY_DELAY_TIME;
|
||||||
|
use crate::notify::types::NOTIFY_TEMPORARY_MAX_TIME;
|
||||||
|
use crate::notify::types::NotificationRulesMap;
|
||||||
|
use crate::notify::types::NotifyEvent;
|
||||||
|
use crate::notify::types::NotifyEventReceiver;
|
||||||
|
use crate::notify::types::NotifyEventSender;
|
||||||
|
use crate::notify::types::NotifyPluginMap;
|
||||||
use crate::{EventSender, ExecutionContext};
|
use crate::{EventSender, ExecutionContext};
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::Hash;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
mod types;
|
||||||
use error::NotifyError;
|
use error::NotifyError;
|
||||||
|
|
||||||
fn validate(finalize: &FinalizeConfig) -> anyhow::Result<()> {
|
fn validate(finalize: &FinalizeConfig) -> anyhow::Result<()> {
|
||||||
@@ -31,8 +32,6 @@ fn validate(finalize: &FinalizeConfig) -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type NotifyPluginMap = HashMap<String, FinalizePlugin>;
|
|
||||||
|
|
||||||
fn extract_plugin(finalize: &FinalizeConfig) -> HashSet<String> {
|
fn extract_plugin(finalize: &FinalizeConfig) -> HashSet<String> {
|
||||||
finalize
|
finalize
|
||||||
.notification
|
.notification
|
||||||
@@ -43,60 +42,6 @@ fn extract_plugin(finalize: &FinalizeConfig) -> HashSet<String> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
pub struct NotifyEvent {
|
|
||||||
id: Uuid,
|
|
||||||
stage: StagePhase,
|
|
||||||
substage: String,
|
|
||||||
result: StageResult,
|
|
||||||
not_before: DateTime<Utc>,
|
|
||||||
priority: u32,
|
|
||||||
effective_priority: u32,
|
|
||||||
lives: u32,
|
|
||||||
max_lives: u32,
|
|
||||||
target: HashSet<String>, // Target audience
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for NotifyEvent {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.id.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for NotifyEvent {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for NotifyEvent {}
|
|
||||||
|
|
||||||
impl PartialOrd for NotifyEvent {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(
|
|
||||||
self.effective_priority
|
|
||||||
.cmp(&other.effective_priority)
|
|
||||||
.then(self.not_before.cmp(&other.not_before))
|
|
||||||
.reverse(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for NotifyEvent {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.partial_cmp(other).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type BundledNotifyEvent = Vec<NotifyEvent>;
|
|
||||||
type NotifyEventReceiver = tokio::sync::mpsc::Receiver<BundledNotifyEvent>;
|
|
||||||
pub type NotifyEventSender = tokio::sync::mpsc::Sender<NotifyEvent>;
|
|
||||||
|
|
||||||
const NOTIFY_NOTREADY_DELAY: std::time::Duration = std::time::Duration::from_secs(5);
|
|
||||||
const NOTIFY_TEMPORARY_DELAY_FACTOR: f64 = 2.0;
|
|
||||||
const NOTIFY_TEMPORARY_DELAY_TIME: f64 = 5.0;
|
|
||||||
const NOTIFY_TEMPORARY_MAX_TIME: f64 = f64::MAX;
|
|
||||||
|
|
||||||
pub async fn notify(
|
pub async fn notify(
|
||||||
finalize_path: &Path,
|
finalize_path: &Path,
|
||||||
ctx: &mut ExecutionContext,
|
ctx: &mut ExecutionContext,
|
||||||
@@ -124,53 +69,54 @@ pub async fn notify(
|
|||||||
}
|
}
|
||||||
let notify_plugins = plugin::register(finalize.plugin, Some(&plugin_list))?;
|
let notify_plugins = plugin::register(finalize.plugin, Some(&plugin_list))?;
|
||||||
|
|
||||||
loop {
|
// loop {
|
||||||
tokio::select! {
|
// tokio::select! {
|
||||||
Some(events) = notifyevent_rx.recv() => {
|
// Some(events) = notifyevent_rx.recv() => {
|
||||||
match notify_handler(¬ify_plugins, &finalize.notification, &events){
|
// match notify_handler(¬ify_plugins, &finalize.notification, &events){
|
||||||
Ok(_) => {}, // Notification succeeded / failed with fallible
|
// Ok(_) => {}, // Notification succeeded / failed with fallible
|
||||||
Err(e) => {
|
// Err(e) => {
|
||||||
log::debug!("Failed to send notification: {:?}", e);
|
// log::debug!("Failed to send notification: {:?}", e);
|
||||||
match e {
|
// match e {
|
||||||
NotifyError::PluginNotAvailable(_) => {
|
// NotifyError::PluginNotAvailable(_) => {
|
||||||
// Should keep lives, add static delay and give less priority
|
// // Should keep lives, add static delay and give less priority
|
||||||
for mut event in events {
|
// for mut event in events {
|
||||||
event.not_before = Utc::now() + NOTIFY_NOTREADY_DELAY;
|
// event.not_before = Utc::now() + NOTIFY_NOTREADY_DELAY;
|
||||||
event.effective_priority = event.effective_priority.saturating_add(1);
|
// event.effective_priority = event.effective_priority.saturating_add(1);
|
||||||
notifyevent_tx.send(event).await?;
|
// notifyevent_tx.send(event).await?;
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
NotifyError::TemporaryError(_) => {
|
// NotifyError::TemporaryError(_) => {
|
||||||
// Should cost 1 life, add dynamic (exponential) delay
|
// // Should cost 1 life, add dynamic (exponential) delay
|
||||||
for mut event in events {
|
// for mut event in events {
|
||||||
event.lives -= 1;
|
// event.lives -= 1;
|
||||||
event.not_before = Utc::now() + notify_delay(&event);
|
// event.not_before = Utc::now() + notify_delay(&event);
|
||||||
notifyevent_tx.send(event).await?;
|
// notifyevent_tx.send(event).await?;
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
NotifyError::PermanentError(_) => {
|
// NotifyError::PermanentError(_) => {
|
||||||
// Unexpected!
|
// // Unexpected!
|
||||||
// Should be dropped
|
// // Should be dropped
|
||||||
log::error!("None of the configured notification plugins works.");
|
// log::error!("None of the configured notification plugins works.");
|
||||||
log::error!("Message will be dropped.");
|
// log::error!("Message will be dropped.");
|
||||||
// TODO: Send audit/metric information
|
// // TODO: Send audit/metric information
|
||||||
}
|
// }
|
||||||
error => {
|
// error => {
|
||||||
// Unexpected!
|
// // Unexpected!
|
||||||
log::error!("Internal error: notify_handler returned unexpected error: {:?}", error);
|
// log::error!("Internal error: notify_handler returned unexpected error: {:?}", error);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify_handler(
|
fn notification_handler(
|
||||||
plugins: &NotifyPluginMap,
|
plugins: &NotifyPluginMap,
|
||||||
config: &NotificationConfig,
|
config: &NotificationRulesMap,
|
||||||
event: &Vec<NotifyEvent>,
|
event: &BundledNotifyEvent,
|
||||||
) -> Result<(), NotifyError> {
|
) -> Result<(), NotifyError> {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::time::Duration;
|
||||||
|
use crate::finalize::plugin::FinalizePlugin;
|
||||||
|
use crate::types::buildstatus::StagePhase;
|
||||||
|
use crate::types::buildstatus::StageResult;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::finalize::NotificationTemplate;
|
||||||
|
use crate::finalize::config::Trigger;
|
||||||
|
/// Final runtime configuration for notification, restored and flattened from yaml
|
||||||
|
pub struct NotificationRule{
|
||||||
|
pub enabled: bool,
|
||||||
|
|
||||||
|
pub trigger: Vec<Trigger>,
|
||||||
|
|
||||||
|
pub template: NotificationTemplate,
|
||||||
|
|
||||||
|
/// Whether notification failure should not trigger next priority group
|
||||||
|
pub fallible: bool,
|
||||||
|
|
||||||
|
/// Maximum number of attempts
|
||||||
|
pub max_lives: u32,
|
||||||
|
|
||||||
|
/// Timeout for notification execution
|
||||||
|
pub timeout: Duration,
|
||||||
|
|
||||||
|
/// Plugin-specific configuration
|
||||||
|
pub config: serde_yaml::Value,
|
||||||
|
|
||||||
|
/// Factor of exponential backoff for retry delay
|
||||||
|
// Not provided for external use
|
||||||
|
pub retry_backoff: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Priority = u32;
|
||||||
|
pub type Method = String;
|
||||||
|
pub type NotificationRulesMap = HashMap<Priority, HashMap<Method, Vec<NotificationRule>>>;
|
||||||
|
pub type BundledNotifyEvent = Vec<NotifyEvent>;
|
||||||
|
pub type NotifyEventReceiver = tokio::sync::mpsc::Receiver<BundledNotifyEvent>;
|
||||||
|
pub type NotifyEventSender = tokio::sync::mpsc::Sender<NotifyEvent>;
|
||||||
|
pub type NotifyPluginMap = HashMap<String, FinalizePlugin>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct NotifyEvent {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub stage: StagePhase,
|
||||||
|
pub substage: String,
|
||||||
|
pub result: StageResult,
|
||||||
|
pub not_before: DateTime<Utc>,
|
||||||
|
pub priority: u32,
|
||||||
|
pub effective_priority: u32,
|
||||||
|
pub lives: u32,
|
||||||
|
pub max_lives: u32,
|
||||||
|
pub target: HashSet<String>, // Target audience
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for NotifyEvent {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for NotifyEvent {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for NotifyEvent {}
|
||||||
|
|
||||||
|
impl PartialOrd for NotifyEvent {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(
|
||||||
|
self.effective_priority
|
||||||
|
.cmp(&other.effective_priority)
|
||||||
|
.then(self.not_before.cmp(&other.not_before))
|
||||||
|
.reverse(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for NotifyEvent {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.partial_cmp(other).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub const NOTIFY_NOTREADY_DELAY: std::time::Duration = std::time::Duration::from_secs(5);
|
||||||
|
pub const NOTIFY_TEMPORARY_DELAY_FACTOR: f64 = 2.0;
|
||||||
|
pub const NOTIFY_TEMPORARY_DELAY_TIME: f64 = 5.0;
|
||||||
|
pub const NOTIFY_TEMPORARY_MAX_TIME: f64 = f64::MAX;
|
||||||
Reference in New Issue
Block a user