11from dataclasses import dataclass , field
2- from datetime import timedelta
2+ from datetime import datetime , timedelta
33from enum import Enum
44from typing import Any , Dict , Optional , Union
55
1010STREAM_FILTER_SPEC = "rabbitmq:stream-filter"
1111STREAM_OFFSET_SPEC = "rabbitmq:stream-offset-spec"
1212STREAM_FILTER_MATCH_UNFILTERED = "rabbitmq:stream-match-unfiltered"
13+ AMQP_PROPERTIES_FILTER = "amqp:properties-filter"
1314
1415
1516@dataclass
@@ -149,6 +150,42 @@ class ExchangeToExchangeBindingSpecification:
149150 binding_key : Optional [str ] = None
150151
151152
153+ @dataclass
154+ class MessageProperties :
155+ """
156+ Properties for an AMQP message.
157+
158+ Attributes:
159+ message_id: Uniquely identifies a message within the system (int, UUID, bytes, or str).
160+ user_id: Identity of the user responsible for producing the message.
161+ to: Intended destination node of the message.
162+ subject: Summary information about the message content and purpose.
163+ reply_to: Address of the node to send replies to.
164+ correlation_id: Client-specific id for marking or identifying messages (int, UUID, bytes, or str).
165+ content_type: RFC-2046 MIME type for the message's body.
166+ content_encoding: Modifier to the content-type.
167+ absolute_expiry_time: Absolute time when the message expires.
168+ creation_time: Absolute time when the message was created.
169+ group_id: Group the message belongs to.
170+ group_sequence: Relative position of this message within its group.
171+ reply_to_group_id: Id for sending replies to a specific group.
172+ """
173+
174+ message_id : Optional [Union [int , str , bytes ]] = None
175+ user_id : Optional [bytes ] = None
176+ to : Optional [str ] = None
177+ subject : Optional [str ] = None
178+ reply_to : Optional [str ] = None
179+ correlation_id : Optional [Union [int , str , bytes ]] = None
180+ content_type : Optional [str ] = None
181+ content_encoding : Optional [str ] = None
182+ absolute_expiry_time : Optional [datetime ] = None
183+ creation_time : Optional [datetime ] = None
184+ group_id : Optional [str ] = None
185+ group_sequence : Optional [int ] = None
186+ reply_to_group_id : Optional [str ] = None
187+
188+
152189"""
153190 StreamFilterOptions defines the filtering options for a stream consumer.
154191 for values and match_unfiltered see: https://www.rabbitmq.com/blog/2023/10/16/stream-filtering
@@ -159,18 +196,21 @@ class StreamFilterOptions:
159196 values : Optional [list [str ]] = None
160197 match_unfiltered : bool = False
161198 application_properties : Optional [dict [str , Any ]] = None
199+ message_properties : Optional [MessageProperties ] = None
162200 sql : str = ""
163201
164202 def __init__ (
165203 self ,
166204 values : Optional [list [str ]] = None ,
167205 match_unfiltered : bool = False ,
168206 application_properties : Optional [dict [str , Any ]] = None ,
207+ message_properties : Optional [MessageProperties ] = None ,
169208 sql : str = "" ,
170209 ):
171210 self .values = values
172211 self .match_unfiltered = match_unfiltered
173212 self .application_properties = application_properties
213+ self .message_properties = message_properties
174214 self .sql = sql
175215
176216
@@ -195,27 +235,23 @@ def __init__(
195235 filter_options : Optional [StreamFilterOptions ] = None ,
196236 ):
197237
198- self .streamFilterOptions = filter_options
238+ self ._filter_set : Dict [ symbol , Described ] = {}
199239
200- if offset_specification is None and self . streamFilterOptions is None :
240+ if offset_specification is None and filter_options is None :
201241 raise ValidationCodeException (
202242 "At least one between offset_specification and filters must be set when setting up filtering"
203243 )
204- self ._filter_set : Dict [symbol , Described ] = {}
205244 if offset_specification is not None :
206245 self ._offset (offset_specification )
207246
208- if (
209- self .streamFilterOptions is not None
210- and self .streamFilterOptions .values is not None
211- ):
212- self ._filter_values (self .streamFilterOptions .values )
247+ if filter_options is not None and filter_options .values is not None :
248+ self ._filter_values (filter_options .values )
213249
214- if (
215- self .streamFilterOptions is not None
216- and self . streamFilterOptions . match_unfiltered
217- ) :
218- self ._filter_match_unfiltered ( self . streamFilterOptions . match_unfiltered )
250+ if filter_options is not None and filter_options . match_unfiltered :
251+ self ._filter_match_unfiltered ( filter_options . match_unfiltered )
252+
253+ if filter_options is not None and filter_options . message_properties is not None :
254+ self ._filter_message_properties ( filter_options . message_properties )
219255
220256 def _offset (self , offset_specification : Union [OffsetSpecification , int ]) -> None :
221257 """
@@ -257,6 +293,29 @@ def _filter_match_unfiltered(self, filter_match_unfiltered: bool) -> None:
257293 symbol (STREAM_FILTER_MATCH_UNFILTERED ), filter_match_unfiltered
258294 )
259295
296+ def _filter_message_properties (
297+ self , message_properties : Optional [MessageProperties ]
298+ ) -> None :
299+ """
300+ Set AMQP message properties for filtering.
301+
302+ Args:
303+ message_properties: MessageProperties object containing AMQP message properties
304+ """
305+ if message_properties is not None :
306+ # dictionary of symbols and described
307+ filter_prop : Dict [symbol , Any ] = {}
308+
309+ for key , value in message_properties .__dict__ .items ():
310+ if value is not None :
311+ # replace _ with - for the key
312+ filter_prop [symbol (key .replace ("_" , "-" ))] = value
313+
314+ if len (filter_prop ) > 0 :
315+ self ._filter_set [symbol (AMQP_PROPERTIES_FILTER )] = Described (
316+ symbol (AMQP_PROPERTIES_FILTER ), filter_prop
317+ )
318+
260319 def filter_set (self ) -> Dict [symbol , Described ]:
261320 """
262321 Get the current filter set configuration.
0 commit comments