correctly processing enforcements

This commit is contained in:
2026-02-26 15:35:39 -06:00
parent b43495b26d
commit 570c52e623
4 changed files with 84 additions and 4 deletions

View File

@@ -11,15 +11,15 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use attune_common::{ use attune_common::{
models::{Enforcement, Event, Rule}, models::{Enforcement, EnforcementStatus, Event, Rule},
mq::{ mq::{
Consumer, EnforcementCreatedPayload, ExecutionRequestedPayload, MessageEnvelope, Publisher, Consumer, EnforcementCreatedPayload, ExecutionRequestedPayload, MessageEnvelope, Publisher,
}, },
repositories::{ repositories::{
event::{EnforcementRepository, EventRepository}, event::{EnforcementRepository, EventRepository, UpdateEnforcementInput},
execution::{CreateExecutionInput, ExecutionRepository}, execution::{CreateExecutionInput, ExecutionRepository},
rule::RuleRepository, rule::RuleRepository,
Create, FindById, Create, FindById, Update,
}, },
}; };
@@ -144,11 +144,40 @@ impl EnforcementProcessor {
&rule, &rule,
) )
.await?; .await?;
// Update enforcement status to Processed after successful execution creation
EnforcementRepository::update(
pool,
enforcement_id,
UpdateEnforcementInput {
status: Some(EnforcementStatus::Processed),
payload: None,
},
)
.await?;
debug!("Updated enforcement {} status to Processed", enforcement_id);
} else { } else {
info!( info!(
"Skipping execution creation for enforcement: {}", "Skipping execution creation for enforcement: {}",
enforcement_id enforcement_id
); );
// Update enforcement status to Disabled since it was not actionable
EnforcementRepository::update(
pool,
enforcement_id,
UpdateEnforcementInput {
status: Some(EnforcementStatus::Disabled),
payload: None,
},
)
.await?;
debug!(
"Updated enforcement {} status to Disabled (skipped)",
enforcement_id
);
} }
Ok(()) Ok(())
@@ -215,7 +244,8 @@ impl EnforcementProcessor {
); );
bail!( bail!(
"Rule {} references a deleted action (action_ref: {})", "Rule {} references a deleted action (action_ref: {})",
rule.id, rule.action_ref rule.id,
rule.action_ref
); );
} }
}; };

View File

@@ -15,6 +15,7 @@ const NOTIFICATION_CHANNELS: &[&str] = &[
"inquiry_created", "inquiry_created",
"inquiry_responded", "inquiry_responded",
"enforcement_created", "enforcement_created",
"enforcement_status_changed",
"event_created", "event_created",
"workflow_execution_status_changed", "workflow_execution_status_changed",
]; ];
@@ -167,6 +168,8 @@ mod tests {
fn test_notification_channels_defined() { fn test_notification_channels_defined() {
assert!(!NOTIFICATION_CHANNELS.is_empty()); assert!(!NOTIFICATION_CHANNELS.is_empty());
assert!(NOTIFICATION_CHANNELS.contains(&"execution_status_changed")); assert!(NOTIFICATION_CHANNELS.contains(&"execution_status_changed"));
assert!(NOTIFICATION_CHANNELS.contains(&"enforcement_created"));
assert!(NOTIFICATION_CHANNELS.contains(&"enforcement_status_changed"));
assert!(NOTIFICATION_CHANNELS.contains(&"inquiry_created")); assert!(NOTIFICATION_CHANNELS.contains(&"inquiry_created"));
} }

View File

@@ -180,6 +180,47 @@ CREATE TRIGGER enforcement_created_notify
COMMENT ON FUNCTION notify_enforcement_created() IS 'Sends enforcement creation notifications via PostgreSQL LISTEN/NOTIFY'; COMMENT ON FUNCTION notify_enforcement_created() IS 'Sends enforcement creation notifications via PostgreSQL LISTEN/NOTIFY';
-- Function to notify on enforcement status changes
CREATE OR REPLACE FUNCTION notify_enforcement_status_changed()
RETURNS TRIGGER AS $$
DECLARE
payload JSON;
BEGIN
-- Only notify on updates when status actually changed
IF TG_OP = 'UPDATE' AND OLD.status IS DISTINCT FROM NEW.status THEN
payload := json_build_object(
'entity_type', 'enforcement',
'entity_id', NEW.id,
'id', NEW.id,
'rule', NEW.rule,
'rule_ref', NEW.rule_ref,
'trigger_ref', NEW.trigger_ref,
'event', NEW.event,
'status', NEW.status,
'old_status', OLD.status,
'condition', NEW.condition,
'conditions', NEW.conditions,
'config', NEW.config,
'payload', NEW.payload,
'created', NEW.created,
'updated', NEW.updated
);
PERFORM pg_notify('enforcement_status_changed', payload::text);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Trigger on enforcement table for status changes
CREATE TRIGGER enforcement_status_changed_notify
AFTER UPDATE ON enforcement
FOR EACH ROW
EXECUTE FUNCTION notify_enforcement_status_changed();
COMMENT ON FUNCTION notify_enforcement_status_changed() IS 'Sends enforcement status change notifications via PostgreSQL LISTEN/NOTIFY';
-- ============================================================================ -- ============================================================================
-- INQUIRY NOTIFICATIONS -- INQUIRY NOTIFICATIONS
-- ============================================================================ -- ============================================================================

View File

@@ -90,6 +90,12 @@ export function useEnforcementStream(
// Extract enforcement data from notification payload (flat structure) // Extract enforcement data from notification payload (flat structure)
const enforcementData = notification.payload as any; const enforcementData = notification.payload as any;
// Invalidate history queries so the EntityHistoryPanel picks up new records
// (e.g. status changes recorded by the enforcement_history trigger)
queryClient.invalidateQueries({
queryKey: ["history", "enforcement", notification.entity_id],
});
// Update specific enforcement query if it exists // Update specific enforcement query if it exists
queryClient.setQueryData( queryClient.setQueryData(
["enforcements", notification.entity_id], ["enforcements", notification.entity_id],