Skip to content

Commit 7269a90

Browse files
YijunXieMSKieranBrantnerMageeyunhaoling
authored
[Service Bus] Service Bus Namespace API Topic, Subscription, Rule and NamespaceProperties (#12227)
* Since resource names can be programmatically constructed, add scrubber at base preparer level so as to ensure recordings get the real resource name swapped out with a consistent moniker. * In CI (but not locally) self.test_class_instance was not set before _preparer_wrapper is called, and thus self.is_live was failing. Since we have the test_class_instance, let's just use that. * Merge SB management client to central repo dev branch (#12205) * resource_moniker isn't guaranteed to be populated, so use moniker instead for default scrubbing strategy. Add remaining notes for how to run Keyvault tests to mgmt_settings_fake. * Update validation and type hints * Update models from swagger * Fix pylint * Fix update_subscription * Fix mypy errors * Remove start_index and max_page_size of list operations * add test and fix minor bugs * fix pylint * skip test of list with param and update recordings * Update msrest dependency to 0.6.17 * Fix code review feedback * Fix pylint and mypy error * Small fix type annotation * Do not call copy() in update_xxx * Update ivar * Link to swagger file in repo * clear queue/topic before the actual test Co-authored-by: Kieran Brantner-Magee <[email protected]> Co-authored-by: Adam Ling <[email protected]>
1 parent ca9eacd commit 7269a90

File tree

127 files changed

+23101
-17198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+23101
-17198
lines changed

sdk/servicebus/azure-servicebus/azure/servicebus/aio/management/_management_client_async.py

+850
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
from typing import cast
6+
from xml.etree.ElementTree import ElementTree
7+
8+
9+
import urllib.parse as urlparse
10+
11+
from azure.servicebus.management import _constants as constants
12+
from ...management._handle_response_error import _handle_response_error
13+
14+
# This module defines functions get_next_template and extract_data_template.
15+
# Application code uses functools.partial to substantialize their params and builds an
16+
# azure.core.async_paging.AsyncItemPaged instance with the two substantialized functions.
17+
18+
# The following is an ATOM feed XML list of QueueDescription with page size = 2.
19+
# Tag <feed> has 2 (the page size) children <entry> tags.
20+
# Tag <link rel="next" .../> tells the link to the next page.
21+
# The whole XML will be deserialized into an XML ElementTree.
22+
# Then model class QueueDescriptionFeed deserializes the ElementTree into a QueueDescriptionFeed instance.
23+
# (QueueDescriptionFeed is defined in file ../../management/_generated/models/_models.py and _models_py3.py)
24+
# Function get_next_template gets the next page of XML data like this one and returns the ElementTree.
25+
# Function extract_data_template deserialize data from the ElementTree and provide link to the next page.
26+
# azure.core.async_paging.AsyncItemPaged orchestrates the data flow between them.
27+
28+
# <feed xmlns="http://www.w3.org/2005/Atom">
29+
# <title type="text">Queues</title>
30+
# <id>https://servicebusname.servicebus.windows.net/$Resources/queues?$skip=0&amp;$top=2&amp;api-version=2017-04</id>
31+
# <updated>2020-06-30T23:49:41Z</updated>
32+
# <link rel="self" href="https://servicebusname.servicebus.windows.net/$Resources/queues?
33+
# $skip=0&amp;$top=2&amp;api-version=2017-04"/>
34+
# <link rel="next" href="https://servicebusname.servicebus.windows.net/$Resources/queues?
35+
# %24skip=2&amp;%24top=2&amp;api-version=2017-04"/>
36+
#
37+
# <entry xml:base="https://servicebusname.servicebus.windows.net/$Resources/queues?
38+
# $skip=0&amp;$top=2&amp;api-version=2017-04">
39+
# <id>https://servicebusname.servicebus.windows.net/5?api-version=2017-04</id>
40+
# <title type="text">5</title>
41+
# <published>2020-06-05T00:24:34Z</published>
42+
# <updated>2020-06-25T05:57:29Z</updated>
43+
# <author>
44+
# <name>servicebusname</name>
45+
# </author>
46+
# <link rel="self" href="../5?api-version=2017-04"/>
47+
# <content type="application/xml">
48+
# <QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect"
49+
# xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
50+
# ...
51+
# </QueueDescription>
52+
# </content>
53+
# </entry>
54+
# <entry xml:base="https://servicebusname.servicebus.windows.net/$Resources/queues?
55+
# $skip=0&amp;$top=2&amp;api-version=2017-04">
56+
# <id>https://servicebusname.servicebus.windows.net/6?api-version=2017-04</id>
57+
# <title type="text">6</title>
58+
# <published>2020-06-15T19:49:35Z</published>
59+
# <updated>2020-06-15T19:49:35Z</updated>
60+
# <author>
61+
# <name>servicebusname</name>
62+
# </author>
63+
# <link rel="self" href="../6?api-version=2017-04"/>
64+
# <content type="application/xml">
65+
# <QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect"
66+
# xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
67+
# ...
68+
# </QueueDescription>
69+
# </content>
70+
# </entry>
71+
# </feed>
72+
73+
async def extract_data_template(feed_class, convert, feed_element):
74+
"""A function that will be partialized to build a function used by AsyncItemPaged.
75+
76+
It deserializes the ElementTree returned from function `get_next_template`, returns data in an iterator and
77+
the link to next page.
78+
79+
azure.core.async_paging.AsyncItemPaged will use the returned next page to call a partial function created
80+
from `get_next_template` to fetch data of next page.
81+
82+
"""
83+
deserialized = feed_class.deserialize(feed_element)
84+
list_of_qd = [convert(x) if convert else x for x in deserialized.entry]
85+
next_link = None
86+
# when the response xml has two <link> tags, the 2nd if the next-page link.
87+
if deserialized.link and len(deserialized.link) == 2:
88+
next_link = deserialized.link[1].href
89+
return next_link, iter(list_of_qd) # when next_page is None, AsyncPagedItem will stop fetch next page data.
90+
91+
92+
async def get_next_template(list_func, *args, start_index=0, max_page_size=100, **kwargs):
93+
"""Call list_func to get the XML data and deserialize it to XML ElementTree.
94+
95+
azure.core.async_paging.AsyncItemPaged will call `extract_data_template` and use the returned
96+
XML ElementTree to call a partial function created from `extrat_data_template`.
97+
98+
"""
99+
api_version = constants.API_VERSION
100+
if args[0]: # It's next link. It's None for the first page.
101+
queries = urlparse.parse_qs(urlparse.urlparse(args[0]).query)
102+
start_index = int(queries[constants.LIST_OP_SKIP][0])
103+
max_page_size = int(queries[constants.LIST_OP_TOP][0])
104+
api_version = queries[constants.API_VERSION_PARAM_NAME][0]
105+
with _handle_response_error():
106+
feed_element = cast(
107+
ElementTree,
108+
await list_func(
109+
skip=start_index, top=max_page_size,
110+
api_version=api_version,
111+
**kwargs
112+
)
113+
)
114+
return feed_element

sdk/servicebus/azure-servicebus/azure/servicebus/management/__init__.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,29 @@
55

66
from ._management_client import ServiceBusManagementClient
77
from ._generated.models import AuthorizationRule, MessageCountDetails, \
8-
AccessRights, EntityAvailabilityStatus, EntityStatus
9-
from ._models import QueueRuntimeInfo, QueueDescription
8+
AccessRights, EntityAvailabilityStatus, EntityStatus, \
9+
NamespaceProperties, MessagingSku, NamespaceType
10+
11+
from ._models import QueueRuntimeInfo, QueueDescription, TopicRuntimeInfo, TopicDescription, \
12+
SubscriptionDescription, SubscriptionRuntimeInfo, RuleDescription, \
13+
TrueRuleFilter, FalseRuleFilter, SqlRuleFilter, CorrelationRuleFilter, \
14+
SqlRuleAction
1015

1116
__all__ = [
12-
"ServiceBusManagementClient",
17+
'ServiceBusManagementClient',
1318
'AuthorizationRule',
1419
'MessageCountDetails',
1520
'QueueDescription',
1621
'QueueRuntimeInfo',
22+
'TopicDescription',
23+
'TopicRuntimeInfo',
24+
'SubscriptionDescription',
25+
'SubscriptionRuntimeInfo',
1726
'AccessRights',
1827
'EntityAvailabilityStatus',
1928
'EntityStatus',
29+
'RuleDescription',
30+
'CorrelationRuleFilter', 'SqlRuleFilter', 'TrueRuleFilter', 'FalseRuleFilter',
31+
'SqlRuleAction',
32+
'NamespaceProperties', 'MessagingSku', 'NamespaceType',
2033
]

sdk/servicebus/azure-servicebus/azure/servicebus/management/_constants.py

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for license information.
44
# --------------------------------------------------------------------------------------------
55

6+
API_VERSION_PARAM_NAME = "api-version"
67
API_VERSION = "2017-04"
78
ENTRY_TAG = "{http://www.w3.org/2005/Atom}entry"
89
CONTENT_TAG = "{http://www.w3.org/2005/Atom}content"
@@ -11,3 +12,7 @@
1112
TITLE_TAG = "{http://www.w3.org/2005/Atom}title"
1213

1314
ENTITY_TYPE_QUEUES = "queues"
15+
ENTITY_TYPE_TOPICS = "topics"
16+
17+
LIST_OP_SKIP = "$skip"
18+
LIST_OP_TOP = "$top"

sdk/servicebus/azure-servicebus/azure/servicebus/management/_generated/_service_bus_management_client.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,25 @@
1616
from typing import Any, Optional
1717

1818
from ._configuration import ServiceBusManagementClientConfiguration
19-
from .operations import QueueOperations
19+
from .operations import EntityOperations
2020
from .operations import ServiceBusManagementClientOperationsMixin
21+
from .operations import SubscriptionOperations
22+
from .operations import RuleOperations
23+
from .operations import NamespaceOperations
2124
from . import models
2225

2326

2427
class ServiceBusManagementClient(ServiceBusManagementClientOperationsMixin):
2528
"""Azure Service Bus client for managing Queues, Topics, and Subscriptions.
2629
27-
:ivar queue: QueueOperations operations
28-
:vartype queue: azure.servicebus.management._generated.operations.QueueOperations
30+
:ivar entity: EntityOperations operations
31+
:vartype entity: azure.servicebus.management._generated.operations.EntityOperations
32+
:ivar subscription: SubscriptionOperations operations
33+
:vartype subscription: azure.servicebus.management._generated.operations.SubscriptionOperations
34+
:ivar rule: RuleOperations operations
35+
:vartype rule: azure.servicebus.management._generated.operations.RuleOperations
36+
:ivar namespace: NamespaceOperations operations
37+
:vartype namespace: azure.servicebus.management._generated.operations.NamespaceOperations
2938
:param endpoint: The Service Bus fully qualified domain name.
3039
:type endpoint: str
3140
:keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present.
@@ -45,7 +54,13 @@ def __init__(
4554
self._serialize = Serializer(client_models)
4655
self._deserialize = Deserializer(client_models)
4756

48-
self.queue = QueueOperations(
57+
self.entity = EntityOperations(
58+
self._client, self._config, self._serialize, self._deserialize)
59+
self.subscription = SubscriptionOperations(
60+
self._client, self._config, self._serialize, self._deserialize)
61+
self.rule = RuleOperations(
62+
self._client, self._config, self._serialize, self._deserialize)
63+
self.namespace = NamespaceOperations(
4964
self._client, self._config, self._serialize, self._deserialize)
5065

5166
def close(self):

sdk/servicebus/azure-servicebus/azure/servicebus/management/_generated/aio/_service_bus_management_client_async.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,25 @@
1212
from msrest import Deserializer, Serializer
1313

1414
from ._configuration_async import ServiceBusManagementClientConfiguration
15-
from .operations_async import QueueOperations
15+
from .operations_async import EntityOperations
1616
from .operations_async import ServiceBusManagementClientOperationsMixin
17+
from .operations_async import SubscriptionOperations
18+
from .operations_async import RuleOperations
19+
from .operations_async import NamespaceOperations
1720
from .. import models
1821

1922

2023
class ServiceBusManagementClient(ServiceBusManagementClientOperationsMixin):
2124
"""Azure Service Bus client for managing Queues, Topics, and Subscriptions.
2225
23-
:ivar queue: QueueOperations operations
24-
:vartype queue: azure.servicebus.management._generated.aio.operations_async.QueueOperations
26+
:ivar entity: EntityOperations operations
27+
:vartype entity: azure.servicebus.management._generated.aio.operations_async.EntityOperations
28+
:ivar subscription: SubscriptionOperations operations
29+
:vartype subscription: azure.servicebus.management._generated.aio.operations_async.SubscriptionOperations
30+
:ivar rule: RuleOperations operations
31+
:vartype rule: azure.servicebus.management._generated.aio.operations_async.RuleOperations
32+
:ivar namespace: NamespaceOperations operations
33+
:vartype namespace: azure.servicebus.management._generated.aio.operations_async.NamespaceOperations
2534
:param endpoint: The Service Bus fully qualified domain name.
2635
:type endpoint: str
2736
:keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present.
@@ -40,7 +49,13 @@ def __init__(
4049
self._serialize = Serializer(client_models)
4150
self._deserialize = Deserializer(client_models)
4251

43-
self.queue = QueueOperations(
52+
self.entity = EntityOperations(
53+
self._client, self._config, self._serialize, self._deserialize)
54+
self.subscription = SubscriptionOperations(
55+
self._client, self._config, self._serialize, self._deserialize)
56+
self.rule = RuleOperations(
57+
self._client, self._config, self._serialize, self._deserialize)
58+
self.namespace = NamespaceOperations(
4459
self._client, self._config, self._serialize, self._deserialize)
4560

4661
async def close(self) -> None:

sdk/servicebus/azure-servicebus/azure/servicebus/management/_generated/aio/operations_async/__init__.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@
66
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
77
# --------------------------------------------------------------------------
88

9-
from ._queue_operations_async import QueueOperations
9+
from ._entity_operations_async import EntityOperations
1010
from ._service_bus_management_client_operations_async import ServiceBusManagementClientOperationsMixin
11+
from ._subscription_operations_async import SubscriptionOperations
12+
from ._rule_operations_async import RuleOperations
13+
from ._namespace_operations_async import NamespaceOperations
1114

1215
__all__ = [
13-
'QueueOperations',
16+
'EntityOperations',
1417
'ServiceBusManagementClientOperationsMixin',
18+
'SubscriptionOperations',
19+
'RuleOperations',
20+
'NamespaceOperations',
1521
]
+24-24
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
T = TypeVar('T')
1818
ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]]
1919

20-
class QueueOperations:
21-
"""QueueOperations async operations.
20+
class EntityOperations:
21+
"""EntityOperations async operations.
2222
2323
You should not instantiate this class directly. Instead, you should create a Client instance that
2424
instantiates it for you and attaches it as an attribute.
@@ -41,17 +41,17 @@ def __init__(self, client, config, serializer, deserializer) -> None:
4141

4242
async def get(
4343
self,
44-
queue_name: str,
44+
entity_name: str,
4545
enrich: Optional[bool] = False,
4646
api_version: Optional[str] = "2017_04",
4747
**kwargs
4848
) -> object:
49-
"""Get the details about the Queue with the given queueName.
49+
"""Get the details about the Queue or Topic with the given entityName.
5050
51-
Get Queue.
51+
Get Queue or Topic.
5252
53-
:param queue_name: The name of the queue relative to the Service Bus namespace.
54-
:type queue_name: str
53+
:param entity_name: The name of the queue or topic relative to the Service Bus namespace.
54+
:type entity_name: str
5555
:param enrich: A query parameter that sets enrich to true or false.
5656
:type enrich: bool
5757
:param api_version: Api Version.
@@ -69,7 +69,7 @@ async def get(
6969
url = self.get.metadata['url'] # type: ignore
7070
path_format_arguments = {
7171
'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True),
72-
'queueName': self._serialize.url("queue_name", queue_name, 'str', min_length=1),
72+
'entityName': self._serialize.url("entity_name", entity_name, 'str', min_length=1),
7373
}
7474
url = self._client.format_url(url, **path_format_arguments)
7575

@@ -100,21 +100,21 @@ async def get(
100100
return cls(pipeline_response, deserialized, {})
101101

102102
return deserialized
103-
get.metadata = {'url': '/{queueName}'} # type: ignore
103+
get.metadata = {'url': '/{entityName}'} # type: ignore
104104

105105
async def put(
106106
self,
107-
queue_name: str,
107+
entity_name: str,
108108
request_body: object,
109109
api_version: Optional[str] = "2017_04",
110110
if_match: Optional[str] = None,
111111
**kwargs
112112
) -> object:
113-
"""Create or update a queue at the provided queuePath.
113+
"""Create or update a queue or topic at the provided entityName.
114114
115-
:param queue_name: The name of the queue relative to the Service Bus namespace.
116-
:type queue_name: str
117-
:param request_body: Parameters required to make or edit a queue.
115+
:param entity_name: The name of the queue or topic relative to the Service Bus namespace.
116+
:type entity_name: str
117+
:param request_body: Parameters required to make or edit a queue or topic.
118118
:type request_body: object
119119
:param api_version: Api Version.
120120
:type api_version: str
@@ -131,13 +131,13 @@ async def put(
131131
cls = kwargs.pop('cls', None) # type: ClsType[object]
132132
error_map = {404: ResourceNotFoundError, 409: ResourceExistsError}
133133
error_map.update(kwargs.pop('error_map', {}))
134-
content_type = kwargs.pop("content_type", "application/xml")
134+
content_type = kwargs.pop("content_type", "application/atom+xml")
135135

136136
# Construct URL
137137
url = self.put.metadata['url'] # type: ignore
138138
path_format_arguments = {
139139
'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True),
140-
'queueName': self._serialize.url("queue_name", queue_name, 'str', min_length=1),
140+
'entityName': self._serialize.url("entity_name", entity_name, 'str', min_length=1),
141141
}
142142
url = self._client.format_url(url, **path_format_arguments)
143143

@@ -178,20 +178,20 @@ async def put(
178178
return cls(pipeline_response, deserialized, {})
179179

180180
return deserialized
181-
put.metadata = {'url': '/{queueName}'} # type: ignore
181+
put.metadata = {'url': '/{entityName}'} # type: ignore
182182

183183
async def delete(
184184
self,
185-
queue_name: str,
185+
entity_name: str,
186186
api_version: Optional[str] = "2017_04",
187187
**kwargs
188188
) -> object:
189-
"""Delete the Queue with the given queueName.
189+
"""Delete the Queue or Topic with the given entityName.
190190
191-
Delete Queue.
191+
Delete Queue or Topic.
192192
193-
:param queue_name: The name of the queue relative to the Service Bus namespace.
194-
:type queue_name: str
193+
:param entity_name: The name of the queue or topic relative to the Service Bus namespace.
194+
:type entity_name: str
195195
:param api_version: Api Version.
196196
:type api_version: str
197197
:keyword callable cls: A custom type or function that will be passed the direct response
@@ -207,7 +207,7 @@ async def delete(
207207
url = self.delete.metadata['url'] # type: ignore
208208
path_format_arguments = {
209209
'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True),
210-
'queueName': self._serialize.url("queue_name", queue_name, 'str', min_length=1),
210+
'entityName': self._serialize.url("entity_name", entity_name, 'str', min_length=1),
211211
}
212212
url = self._client.format_url(url, **path_format_arguments)
213213

@@ -236,4 +236,4 @@ async def delete(
236236
return cls(pipeline_response, deserialized, {})
237237

238238
return deserialized
239-
delete.metadata = {'url': '/{queueName}'} # type: ignore
239+
delete.metadata = {'url': '/{entityName}'} # type: ignore

0 commit comments

Comments
 (0)