Add namespace event filtering (SubscribeWithFilter)
Adds support for filtering events by type or actor pattern within namespace subscriptions. Key changes: - Add SubscriptionFilter type with EventTypes and ActorPattern fields - Add SubscribeWithFilter to EventBroadcaster interface - Implement filtering in EventBus with full wildcard pattern support preserved - Implement filtering in NATSEventBus (server-side namespace, client-side filters) - Add MatchActorPattern function for actor ID pattern matching - Add comprehensive unit tests for all filtering scenarios Filter Processing: - EventTypes: Event must match at least one type in the list (OR within types) - ActorPattern: Event's ActorID must match the pattern (supports * and > wildcards) - Multiple filters are combined with AND logic This implementation works alongside the existing wildcard subscription support: - Namespace wildcards (* and >) work with event filters - Filters are applied after namespace pattern matching - Metrics are properly recorded for filtered subscriptions Closes #21 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit was merged in pull request #54.
This commit is contained in:
@@ -61,11 +61,25 @@ func NewNATSEventBus(nc *nats.Conn) (*NATSEventBus, error) {
|
||||
// Security Warning: Wildcard patterns receive events from all matching namespaces,
|
||||
// bypassing namespace isolation. Only use for trusted system components.
|
||||
func (neb *NATSEventBus) Subscribe(namespacePattern string) <-chan *Event {
|
||||
return neb.SubscribeWithFilter(namespacePattern, nil)
|
||||
}
|
||||
|
||||
// SubscribeWithFilter creates a filtered subscription channel for a namespace pattern.
|
||||
// Events are filtered by the provided SubscriptionFilter before delivery.
|
||||
// If filter is nil or empty, all events matching the namespace pattern are delivered.
|
||||
//
|
||||
// For NATSEventBus:
|
||||
// - Namespace pattern filtering is applied at the NATS level using native wildcards
|
||||
// - EventTypes and ActorPattern filters are applied client-side after receiving messages
|
||||
//
|
||||
// This allows efficient server-side filtering for namespaces while providing
|
||||
// flexible client-side filtering for event types and actors.
|
||||
func (neb *NATSEventBus) SubscribeWithFilter(namespacePattern string, filter *SubscriptionFilter) <-chan *Event {
|
||||
neb.mutex.Lock()
|
||||
defer neb.mutex.Unlock()
|
||||
|
||||
// Create local subscription first
|
||||
ch := neb.EventBus.Subscribe(namespacePattern)
|
||||
// Create local subscription first (with filter)
|
||||
ch := neb.EventBus.SubscribeWithFilter(namespacePattern, filter)
|
||||
|
||||
// Check if this is the first subscriber for this pattern
|
||||
count := neb.patternSubscribers[namespacePattern]
|
||||
@@ -141,12 +155,21 @@ func (neb *NATSEventBus) handleNATSEvent(msg *nats.Msg, subscribedPattern string
|
||||
}
|
||||
|
||||
// deliverToWildcardSubscribers delivers an event to subscribers of a specific wildcard pattern
|
||||
// Applies filters before delivery.
|
||||
func (neb *NATSEventBus) deliverToWildcardSubscribers(pattern string, event *Event) {
|
||||
neb.EventBus.mutex.RLock()
|
||||
defer neb.EventBus.mutex.RUnlock()
|
||||
|
||||
for _, sub := range neb.EventBus.wildcardSubscribers {
|
||||
if sub.pattern == pattern {
|
||||
// Apply filter if present
|
||||
if sub.filter != nil && !sub.filter.IsEmpty() {
|
||||
if !sub.filter.Matches(event) {
|
||||
// Event doesn't match filter, skip delivery
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case sub.ch <- event:
|
||||
// Event delivered from NATS
|
||||
|
||||
Reference in New Issue
Block a user