-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[ServiceBus] Track2 ServiceBus API Proposal #9900
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e115bbd
67d0b04
b598e9d
979f2ec
d853ca4
0d83238
18aa235
3b914d5
a724188
bf5ece0
175f7eb
1fa6128
0558c32
c21dd5e
c1f212f
16eb476
187700f
190a912
68b0e4a
f57e0b4
86e19ef
785bea9
30c37b5
53349f7
17ae9c9
5c58b7c
ce69df8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,285 @@ | ||
| ## APIs: | ||
| class ServiceBusClient: | ||
| def __init__( | ||
| self, | ||
| fully_qualified_namespace : str, | ||
| credential : TokenCredential, | ||
| logging_enable: bool = False, | ||
|
yunhaoling marked this conversation as resolved.
|
||
| http_proxy: dict = None, | ||
| transport_type: TransportType = TransportType.Amqp, | ||
| ): | ||
|
|
||
| def __enter__(self): | ||
| def __exit__(self, *kwargs): | ||
| def close(self): | ||
|
|
||
| @classmethod | ||
| def from_connection_string(cls, conn_str, **kwargs): | ||
|
|
||
| def get_queue_sender(self, queue_name, **kwargs) -> ServiceBusSender: | ||
|
yunhaoling marked this conversation as resolved.
|
||
| def get_queue_receiver(self, queue_name, **kwargs) -> ServiceBusReceiver: | ||
| def get_topic_sender(self, topic_name, **kwargs) -> ServiceBusSender: | ||
| def get_subscription_receiver(self, topic_name, subscription_name, **kwargs) -> ServiceBusReceiver: | ||
|
|
||
| def get_queue_session_receiver(self, queue_name, session_id, **kwargs) -> ServiceBusSessionReceiver: | ||
| def get_subscription_session_receiver(self, topic_name, subscription_name, session_id, **kwargs) -> ServiceBusSessionReceiver: | ||
|
|
||
| def get_subscription_rule_manager(self, topic_name, subscription_name) -> SubscriptionRuleManager: | ||
|
|
||
| class ServiceBusSender: | ||
| def __init__( | ||
| self, | ||
| fully_qualified_namespace : str, | ||
| credential: TokenCredential, | ||
| queue_name: str = None, | ||
| topic_name: str = None, | ||
| logging_enable: bool = False, | ||
| http_proxy: dict = None, | ||
| transport_type: TransportType = TransportType.Amqp, | ||
| retry_total : int = 3, | ||
| retry_backoff_factor : float = 30, | ||
| retry_backoff_maximum : int = 120 # cur_retry_backoff = retry_backoff_factor * (2^cur_retry_time) | ||
| ) -> None: | ||
| @classmethod | ||
| def from_connection_string( | ||
| cls, | ||
| conn_str : str, | ||
| queue_name: str = None, | ||
| topic_name: str = None, | ||
| **kwargs | ||
| ) -> ServiceBusSender: | ||
|
|
||
| def __enter__(self): | ||
| def __exit__(self): | ||
|
|
||
| def close(self) -> None: | ||
|
|
||
| def create_batch( | ||
| self, | ||
| max_size_in_bytes : int = None | ||
| ) -> BatchMessage: | ||
|
|
||
| def send( | ||
| self, | ||
| message : Union[Message, BatchMessage], | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @YijunXieMS - did we go with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @richardpark-msft yes EH has EventDataBatch. For the SB question, I'll leave it to Adam and Kieran. I don't have a lot of SB context for now.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @richardpark-msft I think then the following up question would be: as for the purpose of keeping consistent |
||
| message_timeout : float = None | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the unit for this? And do you really support fractional units or is this really just a whole number (ie, int?)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ms or s? service behavior, each language ecosystem |
||
| ) -> None: | ||
| def schedule( | ||
| self, | ||
| message : Union[Message, BatchMessage], | ||
| schedule_time_utc : datetime, | ||
| ) -> List[int]: | ||
|
|
||
| def cancel_scheduled_messages(self, sequence_number : Union[int, List[int]]) -> None: | ||
|
|
||
|
|
||
| class ServiceBusReceiver: | ||
| def __init__( | ||
| self, | ||
| fully_qualified_namespace : str, | ||
| credential: TokenCredential, | ||
| queue_name: str = None, | ||
| topic_name: str = None, | ||
| subscription_name : str = None, | ||
| session_id : str = None, | ||
| mode : ReceiveSettleMode = ReceiveSettleMode.PeekLock | ||
| logging_enable: bool = False, | ||
| http_proxy: dict = None, | ||
| transport_type: TransportType = TransportType.Amqp, | ||
| retry_total : int = 3, | ||
| retry_backoff_factor: float = 30, | ||
| retry_backoff_maximum: int = 120 # cur_retry_backoff = retry_backoff_factor * (2^cur_retry_time) | ||
| ) -> None: | ||
|
annatisch marked this conversation as resolved.
|
||
| @classmethod | ||
| def from_connection_string( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be considered an internal method? (not sure what your options are here) |
||
| cls, | ||
| conn_str : str, | ||
| queue_name: str = None, | ||
| topic_name: str = None, | ||
| subscription_name : str = None, | ||
| session_id: str = None, | ||
| mode : ReceiveSettleMode = ReceiveSettleMode.PeekLock | ||
| **kwargs | ||
| )-> ServiceBusReceiver: | ||
|
|
||
| def __enter__(self): | ||
| def __exit__(self): | ||
|
|
||
| def close(self) -> None: | ||
|
|
||
| def __iter__(self): | ||
| def __next__(self): | ||
| def next(self): | ||
|
annatisch marked this conversation as resolved.
|
||
|
|
||
| def peek( | ||
| self, | ||
| message_count : int = 1, | ||
| sequence_number : int = None | ||
| ) -> List[PeekMessage]: | ||
| def receive_deferred_messages( | ||
|
yunhaoling marked this conversation as resolved.
|
||
| self, | ||
| sequence_numbers : List[int], | ||
| ) -> List[DeferredMessage]: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yunhaoling mentioned that the only difference is that a |
||
| def receive( | ||
| self, | ||
| max_batch_size : int = None, | ||
| timeout : float = None, | ||
| ) -> List[ReceivedMessage]: # Pull mode receive | ||
|
|
||
|
|
||
| class ServiceBusSessionReceiver(ServiceBusReceiver): | ||
| @property | ||
| def session(self) -> ServiceBusSession: | ||
|
|
||
|
|
||
| class ServiceBusSession: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We renamed this to ServiceBusSessionManager for .NET. |
||
| @property | ||
| def get_session_state(self) -> str: # This can be made to property if property is more pythonic way | ||
| def set_session_state(self, state : str) -> None: # This can be made to property if property is more pythonic way | ||
|
yunhaoling marked this conversation as resolved.
|
||
| def renew_lock(self) -> None: | ||
| @property | ||
| def session_id(self) -> str: | ||
| def expired(self) -> bool: | ||
|
|
||
|
|
||
| class Message: | ||
|
yunhaoling marked this conversation as resolved.
|
||
| def __init__(self, body : str, encoding : str = 'UTF-8', session_id : str = None, **kwargs) -> None: | ||
| def __str__(self): | ||
|
|
||
| # @properties | ||
| def body(self) -> Union[bytes, Generator[bytes]]: # read-only | ||
| def partition_key(self, value : str): | ||
| def partition_key(self) -> str: | ||
| def session_id(self, value : str): | ||
| def session_id(self) -> str: | ||
| def via_partition_key(self, value: str): | ||
| def via_partition_key(self) -> str: | ||
| def time_to_live(self, value : Union[float, timedelta]): | ||
| def time_to_live(self) -> datetime.timedelta: | ||
| def annotations(self, value : dict[str, Any]): | ||
| def annotations(self) -> dict[str, Any]: | ||
| def user_properties(self, value : dict[str, Any]): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed to just Properties in .NET. |
||
| def user_properties(self) -> dict[str, Any]: | ||
| def enqueue_sequence_number(self, value : int): | ||
| def enqueue_sequence_number(self) -> int: | ||
|
|
||
| # Methods | ||
| def schedule(self, schedule_time_utc : datetime) -> None: | ||
|
|
||
|
|
||
| class BatchMessage(Message): | ||
| # inherited from Message | ||
| def add(self, message: Message) -> None: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So if we're going with what we have in EH - this should be 'try_add' and it should return a bool.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In EH Python has |
||
|
|
||
|
|
||
| class PeekMessage(Message): | ||
| def enqueued_time(self) -> datetime: # read-only | ||
| def scheduled_enqueue_time(self) -> datetime: # read-only | ||
| def sequence_number(self) -> int: # read-only | ||
| def partition_id(self) -> str: # read-only | ||
| def session_id(self) -> str: # read-only | ||
|
|
||
| class ReceivedMessage(Message): | ||
|
|
||
| # @properties | ||
| def settled(self) -> bool: # read-only | ||
| def enqueued_time(self) -> datetime: # read-only | ||
| def scheduled_enqueue_time(self) -> datetime: # read-only | ||
| def sequence_number(self) -> int: # read-only | ||
| def partition_id(self) -> str: # read-only | ||
| def locked_until(self) -> datetime: # read-only | ||
| def expired(self) -> bool: # read-only | ||
| def lock_token(self) -> str: # read-only | ||
| def session_id(self) -> str: # read-only | ||
|
|
||
| # methods | ||
| def renew_lock(self) -> None: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Richard Q: throw if receive in ReceiveAndDelete mode
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's a general question - do we throw on "invalid" methods or do we no-op?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. conclusion: throw error on "invalid" methods
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Talk about this on Arch board meeting!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "This" = "should we host the settlement methods (complete, abandon, etc..) on the message or on the client?" (JS has a different answer for this - we have a context object) |
||
| def complete(self) -> None: | ||
| def dead_letter(self, description : str = None) -> None: | ||
| def abandon(self) -> None: | ||
| def defer(self) -> None: | ||
|
|
||
|
|
||
| class ReceiveSettleMode: | ||
| PeekLock | ||
| ReceiveAndDelete | ||
|
|
||
|
|
||
| class TransportType(Enum): | ||
| Amqp | ||
| AmqpOverWebsocket | ||
|
|
||
|
|
||
| class ServiceBusSharedKeyCredential: | ||
| def __init__(self, policy, key): | ||
| def get_token(self, *scopes, **kwargs): | ||
|
|
||
|
yunhaoling marked this conversation as resolved.
|
||
|
|
||
| class AutoLockRenew: | ||
| def __init__(self, executor=None, max_workers=None): | ||
| def __enter__(self): | ||
|
|
||
| def __exit__(self, *args): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this file auto-generated? What happened with this indentation? :) |
||
|
|
||
| def register(self, renewable, timeout=300): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have a rule to suffix timeout with some sort of measurement? Ie, this must be seconds so
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also - what does |
||
|
|
||
|
|
||
| class SubscriptionRuleManager: | ||
| # sql and correlation filter apply to user properties and system properties | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. anna: may be under Receiver Scope? |
||
| # https://docs.microsoft.com/en-us/azure/service-bus-messaging/topic-filters | ||
| # rule action: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-sql-rule-action | ||
| def __init__( | ||
| self, | ||
| fully_qualified_namespace: str, | ||
| topic_name: str, | ||
| subscription_name: str, | ||
| credential: TokenCredential, | ||
| logging_enable: bool = False, | ||
| http_proxy: dict = None, | ||
| transport_type: TransportType = TransportType.Amqp | ||
| ) -> SubscriptionRuleManager | ||
|
|
||
| @classmethod | ||
| def from_connection_string(cls, conn_str, subscription_name, topic_name=None, **kwargs) -> SubscriptionRuleManager: | ||
|
|
||
| def get_rules(self, timeout: float = None) -> list[RuleDescription]: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment on all of these - should
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. list_rules |
||
| def add_rule(self, rule_name: str, filter: Union(bool, str, CorrelationFilter), sql_rule_action_expression: str, timeout: float = None): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. may need to rename to "create_rule"
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (thinking rule as a resource) |
||
| def remove_rule(self, rule_name: str, timeout: float = None): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "delete_rule" |
||
|
|
||
|
|
||
| class RuleDescription: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. anna: making it dict-like object |
||
| #TODO: is it necessary to make it separate class or just a list[dict]? | ||
| def __init__(self): | ||
| @property | ||
| def filter(self) -> Union(str, CorrelationFilter): # TODO: return multiple types OK? | ||
| def action(self) -> str: | ||
| def name(self) -> str: | ||
|
|
||
|
|
||
| class CorrelationFilter: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dict-like object |
||
| # TODO: is it necessary to make it separate class, depending on the fact that there're so many fields, | ||
| # and how people would use this, I personally prefer it being a class | ||
| def __init__(self): | ||
|
|
||
| @property | ||
| def correlation_id(self, val : str): | ||
| def correlation_id(self) -> str: | ||
| def message_id(self, val : str): | ||
| def message_id(self) -> str: | ||
| def to(self, val: str): | ||
| def to(self) -> str: | ||
| def reply_to(self, val : str): | ||
| def reply_to(self) -> str: | ||
| def label(self, val: str): | ||
| def label(self) -> str: | ||
| def session_id(self, val: str): | ||
| def session_id(self) -> str: | ||
| def reply_to_session_id(self, val: str): | ||
| def reply_to_session_id(self) -> str: | ||
| def content_type(self, val: str): | ||
| def content_type(self) -> str: | ||
| def user_properties(self, val: dict): | ||
| def user_properties(self) -> dict: | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this has carried over from eventhubs - from what I can tell the Azure portal always lists it as 'host name'. Should we match the portal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would need service side input
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be called host name in the portal for event hubs as well