From ccd793a7c8849daf54805689136218122f300ba5 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Fri, 23 Oct 2020 13:53:40 +0530 Subject: [PATCH 01/19] initial --- .../servicebus/_connection_string_parser.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py new file mode 100644 index 000000000000..f524dd988bcf --- /dev/null +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py @@ -0,0 +1,69 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +class ServiceBusConnectionStringParser(object): + """ + Parse the connection string. + """ + def __init__(self, conn_str, **kwargs): + """ + :param conn_str: The connection string. + :type conn_str: str + """ + self._conn_str = conn_str + self.fully_qualified_namespace = None + self.endpoint = None + self.entity_path = None + self.shared_access_signature = None + self.shared_access_key_name = None + self.shared_access_key = None + + + def parse(self, **kwargs): + """ + Parse the connection string. + """ + conn_str = self._conn_str.rstrip(";") + conn_settings = [s.split("=", 1) for s in conn_str.split(";")] + if any(len(tup) != 2 for tup in conn_settings): + raise ValueError("Connection string is either blank or malformed.") + conn_settings = dict(conn_settings) + endpoints = _SERVICE_PARAMS[service] + primary = None + secondary = None + if not credential: + try: + credential = {"account_name": conn_settings["AccountName"], "account_key": conn_settings["AccountKey"]} + except KeyError: + credential = conn_settings.get("SharedAccessSignature") + if endpoints["primary"] in conn_settings: + primary = conn_settings[endpoints["primary"]] + if endpoints["secondary"] in conn_settings: + secondary = conn_settings[endpoints["secondary"]] + else: + if endpoints["secondary"] in conn_settings: + raise ValueError("Connection string specifies only secondary endpoint.") + try: + primary = "{}://{}.{}.{}".format( + conn_settings["DefaultEndpointsProtocol"], + conn_settings["AccountName"], + service, + conn_settings["EndpointSuffix"], + ) + secondary = "{}-secondary.{}.{}".format( + conn_settings["AccountName"], service, conn_settings["EndpointSuffix"] + ) + except KeyError: + pass + + if not primary: + try: + primary = "https://{}.{}.{}".format( + conn_settings["AccountName"], service, conn_settings.get("EndpointSuffix", SERVICE_HOST_BASE) + ) + except KeyError: + raise ValueError("Connection string missing required connection details.") + return primary, secondary, credential From 8fbee157a9a1c4c1f84c8030a3391013bb98b4a7 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 26 Oct 2020 22:19:10 +0530 Subject: [PATCH 02/19] sol --- .../azure/servicebus/__init__.py | 8 ++- .../_common/_connection_string_parser.py | 62 +++++++++++++++++ .../servicebus/_connection_string_parser.py | 69 ------------------- .../sync_samples/connection_string_parser.py | 35 ++++++++++ 4 files changed, 104 insertions(+), 70 deletions(-) create mode 100644 sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py delete mode 100644 sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py create mode 100644 sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py b/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py index 18a8cbfc2788..9cb84bae570e 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py @@ -15,6 +15,10 @@ from ._common.message import ServiceBusMessage, ServiceBusMessageBatch, ServiceBusReceivedMessage from ._common.constants import ReceiveMode, SubQueue, NEXT_AVAILABLE_SESSION from ._common.auto_lock_renewer import AutoLockRenewer +from ._common._connection_string_parser import ( + ServiceBusConnectionStringParser, + ServiceBusConnectionStringProperties +) TransportType = constants.TransportType @@ -30,5 +34,7 @@ 'ServiceBusSession', 'ServiceBusSender', 'TransportType', - 'AutoLockRenewer' + 'AutoLockRenewer', + 'ServiceBusConnectionStringParser', + 'ServiceBusConnectionStringProperties' ] diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py new file mode 100644 index 000000000000..5af41e2030c0 --- /dev/null +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -0,0 +1,62 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse # type: ignore + +class ServiceBusConnectionStringProperties(object): + """ + Properties of a connection string + """ + def __init__(self, **kwargs): + self.fully_qualified_namespace = kwargs.pop('fully_qualified_namespace', None) + self.endpoint = kwargs.pop('endpoint', None) + self.entity_path = kwargs.pop('entity_path', None) + self.shared_access_signature = kwargs.pop('shared_access_signature', None) + self.shared_access_key_name = kwargs.pop('shared_access_key_name', None) + self.shared_access_key = kwargs.pop('shared_access_key', None) + + +class ServiceBusConnectionStringParser(object): + """ + Parse the connection string. + """ + def __init__(self, conn_str, **kwargs): + """ + :param conn_str: The connection string to parse. + :type conn_str: str + """ + self._conn_str = conn_str + + def parse(self, **kwargs): + """ + Parse the connection string. + """ + conn_str = self._conn_str.rstrip(";") + conn_settings = [s.split("=", 1) for s in conn_str.split(";")] + if any(len(tup) != 2 for tup in conn_settings): + raise ValueError("Connection string is either blank or malformed.") + conn_settings = dict(conn_settings) + shared_access_signature = conn_settings.get('SharedAccessSignature') + shared_access_key = conn_settings.get('SharedAccessKey') + if shared_access_signature is not None and shared_access_key is not None: + raise ValueError("Only one of the SharedAccessKey or SharedAccessSignature must be present.") + endpoint = conn_settings.get('Endpoint') + if not endpoint: + raise ValueError("Connection string is either blank or malformed.") + parsed = urlparse(endpoint.rstrip('/')) + if parsed.netloc is None: + raise ValueError("Invalid EndPoint on the Connection String.") + namespace = parsed.netloc.strip() + props = { + 'fully_qualified_namespace': namespace, + 'endpoint': endpoint, + 'entity_path': conn_settings.get('EntityPath'), + 'shared_access_signature': shared_access_signature, + 'shared_access_key_name': conn_settings.get('SharedAccessKeyName'), + 'shared_access_key': shared_access_key + } + return ServiceBusConnectionStringProperties(**props) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py deleted file mode 100644 index f524dd988bcf..000000000000 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_connection_string_parser.py +++ /dev/null @@ -1,69 +0,0 @@ -# -------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------------------------- - - -class ServiceBusConnectionStringParser(object): - """ - Parse the connection string. - """ - def __init__(self, conn_str, **kwargs): - """ - :param conn_str: The connection string. - :type conn_str: str - """ - self._conn_str = conn_str - self.fully_qualified_namespace = None - self.endpoint = None - self.entity_path = None - self.shared_access_signature = None - self.shared_access_key_name = None - self.shared_access_key = None - - - def parse(self, **kwargs): - """ - Parse the connection string. - """ - conn_str = self._conn_str.rstrip(";") - conn_settings = [s.split("=", 1) for s in conn_str.split(";")] - if any(len(tup) != 2 for tup in conn_settings): - raise ValueError("Connection string is either blank or malformed.") - conn_settings = dict(conn_settings) - endpoints = _SERVICE_PARAMS[service] - primary = None - secondary = None - if not credential: - try: - credential = {"account_name": conn_settings["AccountName"], "account_key": conn_settings["AccountKey"]} - except KeyError: - credential = conn_settings.get("SharedAccessSignature") - if endpoints["primary"] in conn_settings: - primary = conn_settings[endpoints["primary"]] - if endpoints["secondary"] in conn_settings: - secondary = conn_settings[endpoints["secondary"]] - else: - if endpoints["secondary"] in conn_settings: - raise ValueError("Connection string specifies only secondary endpoint.") - try: - primary = "{}://{}.{}.{}".format( - conn_settings["DefaultEndpointsProtocol"], - conn_settings["AccountName"], - service, - conn_settings["EndpointSuffix"], - ) - secondary = "{}-secondary.{}.{}".format( - conn_settings["AccountName"], service, conn_settings["EndpointSuffix"] - ) - except KeyError: - pass - - if not primary: - try: - primary = "https://{}.{}.{}".format( - conn_settings["AccountName"], service, conn_settings.get("EndpointSuffix", SERVICE_HOST_BASE) - ) - except KeyError: - raise ValueError("Connection string missing required connection details.") - return primary, secondary, credential diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py new file mode 100644 index 000000000000..63844abae391 --- /dev/null +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +""" +An example to show the usage of connection string parser. +""" + +import os +from azure.servicebus import ( + ServiceBusClient, + Message, + ServiceBusConnectionStringProperties, + ServiceBusConnectionStringParser, +) + +from azure.servicebus._base_handler import ServiceBusSharedKeyCredential + +conn_str = os.environ['SERVICE_BUS_CONN_STR'] +QUEUE_NAME = os.environ["SERVICE_BUS_QUEUE_NAME"] + +parse_result = ServiceBusConnectionStringParser(conn_str).parse() + +fully_qualified_namespace = parse_result.fully_qualified_namespace +credential = ServiceBusSharedKeyCredential(parse_result.shared_access_key_name, parse_result.shared_access_key) + +servicebus_client = ServiceBusClient(fully_qualified_namespace, credential) +with servicebus_client: + sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) + with sender: + sender.send_messages(Message('Single Message')) + +print("Send message is done.") From 7594a43a0b7db89eb7ae19cd7569766d2d1379c6 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 26 Oct 2020 23:28:56 +0530 Subject: [PATCH 03/19] changes --- sdk/servicebus/azure-servicebus/CHANGELOG.md | 2 + .../_common/_connection_string_parser.py | 13 +++--- .../sync_samples/connection_string_parser.py | 15 +++++++ .../tests/test_connection_string_parser.py | 44 +++++++++++++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index 40775911ec8a..0785a4c58b11 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -46,6 +46,8 @@ now raise more concrete exception other than `MessageSettleFailed` and `ServiceB * `AutoLockRenewer.register` now takes `ServiceBusReceiver` as a positional parameter. * Removed `encoding` support from `ServiceBusMessage`. +* Added a `ServiceBusConnectionStringParser` which parses a connection string into individual entities. + **BugFixes** * Updated uAMQP dependency to 1.2.12. diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 5af41e2030c0..f8be1abc5ea7 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -9,7 +9,7 @@ class ServiceBusConnectionStringProperties(object): """ - Properties of a connection string + Properties of a connection string. """ def __init__(self, **kwargs): self.fully_qualified_namespace = kwargs.pop('fully_qualified_namespace', None) @@ -21,10 +21,12 @@ def __init__(self, **kwargs): class ServiceBusConnectionStringParser(object): - """ - Parse the connection string. + """Parse the connection string. + + :param conn_str: The connection string that has to be parsed. """ def __init__(self, conn_str, **kwargs): + # type: (str, Any) -> None """ :param conn_str: The connection string to parse. :type conn_str: str @@ -32,6 +34,7 @@ def __init__(self, conn_str, **kwargs): self._conn_str = conn_str def parse(self, **kwargs): + # type(Any) -> ServiceBusConnectionStringProperties """ Parse the connection string. """ @@ -48,8 +51,8 @@ def parse(self, **kwargs): if not endpoint: raise ValueError("Connection string is either blank or malformed.") parsed = urlparse(endpoint.rstrip('/')) - if parsed.netloc is None: - raise ValueError("Invalid EndPoint on the Connection String.") + if not parsed.netloc: + raise ValueError("Invalid Endpoint on the Connection String.") namespace = parsed.netloc.strip() props = { 'fully_qualified_namespace': namespace, diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py index 63844abae391..c7dbdb70c4e3 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py @@ -26,6 +26,7 @@ fully_qualified_namespace = parse_result.fully_qualified_namespace credential = ServiceBusSharedKeyCredential(parse_result.shared_access_key_name, parse_result.shared_access_key) +# initialize with credential and namespace from the parse_result. servicebus_client = ServiceBusClient(fully_qualified_namespace, credential) with servicebus_client: sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) @@ -33,3 +34,17 @@ sender.send_messages(Message('Single Message')) print("Send message is done.") + + +# initialize with DefaultAzureCredential +servicebus_client = ServiceBusClient( + fully_qualified_namespace, + DefaultAzureCredential(), + parse_result.entity_path + ) +with servicebus_client: + sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) + with sender: + sender.send_messages(Message('Single Message')) + +print("Send message is done.") diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py new file mode 100644 index 000000000000..1db2cbea1e72 --- /dev/null +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -0,0 +1,44 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +#-------------------------------------------------------------------------- + +import os +import pytest +from azure.servicebus import ( + ServiceBusClient, + Message, + ServiceBusConnectionStringProperties, + ServiceBusConnectionStringParser, +) + +from devtools_testutils import AzureMgmtTestCase +from azure.servicebus._base_handler import ServiceBusSharedKeyCredential + +class ServiceBusConnectionStringParserTests(AzureMgmtTestCase): + def test_sb_conn_str_parse_cs(self, **kwargs): + conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + parse_result = ServiceBusConnectionStringParser(conn_str).parse() + assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' + assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' + assert parse_result.shared_access_key_name == 'test' + assert parse_result.shared_access_key == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + + def test_sb_conn_str_parse_sas_and_shared_key(self, **kwargs): + conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=;SharedAccessSignature=THISISASASXXXXXXX=' + with pytest.raises(ValueError) as e: + parse_result = ServiceBusConnectionStringParser(conn_str).parse() + assert str(e.value) == 'Only one of the SharedAccessKey or SharedAccessSignature must be present.' + + def test_sb_parse_malformed_conn_str_no_endpoint(self, **kwargs): + conn_str = 'SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + with pytest.raises(ValueError) as e: + parse_result = ServiceBusConnectionStringParser(conn_str).parse() + assert str(e.value) == 'Connection string is either blank or malformed.' + + def test_sb_parse_malformed_conn_str_no_netloc(self, **kwargs): + conn_str = 'Endpoint=MALFORMED;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + with pytest.raises(ValueError) as e: + parse_result = ServiceBusConnectionStringParser(conn_str).parse() + assert str(e.value) == 'Invalid Endpoint on the Connection String.' \ No newline at end of file From 4bb40c470a32d4a8ee203530b9b230c8f4d75fa5 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 26 Oct 2020 23:54:18 +0530 Subject: [PATCH 04/19] lint --- .../azure/servicebus/_common/_connection_string_parser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index f8be1abc5ea7..5e2a2346b5b1 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -25,16 +25,16 @@ class ServiceBusConnectionStringParser(object): :param conn_str: The connection string that has to be parsed. """ - def __init__(self, conn_str, **kwargs): - # type: (str, Any) -> None + def __init__(self, conn_str): + # type: (str) -> None """ :param conn_str: The connection string to parse. :type conn_str: str """ self._conn_str = conn_str - def parse(self, **kwargs): - # type(Any) -> ServiceBusConnectionStringProperties + def parse(self): + # type() -> ServiceBusConnectionStringProperties """ Parse the connection string. """ From e1e42e69ff14fdc5910c31df0e229ace53709782 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 27 Oct 2020 00:05:09 +0530 Subject: [PATCH 05/19] dict mixin --- .../azure/servicebus/_common/_connection_string_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 5e2a2346b5b1..c3b7f36fd0c5 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -7,7 +7,9 @@ except ImportError: from urlparse import urlparse # type: ignore -class ServiceBusConnectionStringProperties(object): +from azure.servicebus.management._models import DictMixin + +class ServiceBusConnectionStringProperties(DictMixin): """ Properties of a connection string. """ From eaa986ae6dab587a887edd84a4e9f37c66c4a5a6 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 27 Oct 2020 18:23:48 +0530 Subject: [PATCH 06/19] comments --- .../_common/_connection_string_parser.py | 57 ++++++++++++++++--- .../tests/test_connection_string_parser.py | 10 +++- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index c3b7f36fd0c5..bcea412430ab 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -14,13 +14,53 @@ class ServiceBusConnectionStringProperties(DictMixin): Properties of a connection string. """ def __init__(self, **kwargs): - self.fully_qualified_namespace = kwargs.pop('fully_qualified_namespace', None) - self.endpoint = kwargs.pop('endpoint', None) - self.entity_path = kwargs.pop('entity_path', None) - self.shared_access_signature = kwargs.pop('shared_access_signature', None) - self.shared_access_key_name = kwargs.pop('shared_access_key_name', None) - self.shared_access_key = kwargs.pop('shared_access_key', None) + self._fully_qualified_namespace = kwargs.pop('fully_qualified_namespace', None) + self._endpoint = kwargs.pop('endpoint', None) + self._entity_path = kwargs.pop('entity_path', None) + self._shared_access_signature = kwargs.pop('shared_access_signature', None) + self._shared_access_key_name = kwargs.pop('shared_access_key_name', None) + self._shared_access_key = kwargs.pop('shared_access_key', None) + + @property + def fully_qualified_namespace(self): + """The fully qualified host name for the Service Bus namespace. + The namespace format is: `.servicebus.windows.net`. + """ + return self._fully_qualified_namespace + + @property + def endpoint(self): + """The endpoint for the Service Bus resource. In the format sb:/// + """ + return self._endpoint + @property + def entity_path(self): + """Optional. Represents the name of the queue/topic. + """ + return self._entity_path + + @property + def shared_access_signature(self): + """ + This can be provided instead of the shared_access_key_name and the shared_access_key. + """ + return self._shared_access_signature + + @property + def shared_access_key_name(self): + """ + The name of the shared access key. + """ + return self._shared_access_key_name + + @property + def shared_access_key(self): + """ + Required for authentication along with the key name. A shared_access_signature can be used + alternatively. + """ + return self._shared_access_key class ServiceBusConnectionStringParser(object): """Parse the connection string. @@ -45,7 +85,10 @@ def parse(self): if any(len(tup) != 2 for tup in conn_settings): raise ValueError("Connection string is either blank or malformed.") conn_settings = dict(conn_settings) - shared_access_signature = conn_settings.get('SharedAccessSignature') + shared_access_signature = None + for key, value in conn_settings.items(): + if key.lower() == 'sharedaccesssignature': + shared_access_signature = value shared_access_key = conn_settings.get('SharedAccessKey') if shared_access_signature is not None and shared_access_key is not None: raise ValueError("Only one of the SharedAccessKey or SharedAccessSignature must be present.") diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py index 1db2cbea1e72..67662252982b 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -41,4 +41,12 @@ def test_sb_parse_malformed_conn_str_no_netloc(self, **kwargs): conn_str = 'Endpoint=MALFORMED;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' with pytest.raises(ValueError) as e: parse_result = ServiceBusConnectionStringParser(conn_str).parse() - assert str(e.value) == 'Invalid Endpoint on the Connection String.' \ No newline at end of file + assert str(e.value) == 'Invalid Endpoint on the Connection String.' + + def test_sb_parse_conn_str_sas(self, **kwargs): + conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessSignature=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + parse_result = ServiceBusConnectionStringParser(conn_str).parse() + assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' + assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' + assert parse_result.shared_access_signature == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + assert parse_result.shared_access_key_name == None From 2bf7babc43b4bb1b1735e4e763cd53a849a57fe2 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 27 Oct 2020 18:24:31 +0530 Subject: [PATCH 07/19] Update sdk/servicebus/azure-servicebus/CHANGELOG.md Co-authored-by: KieranBrantnerMagee --- sdk/servicebus/azure-servicebus/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index 0785a4c58b11..55717fb6e261 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -9,6 +9,7 @@ - `ServiceBusReceiver`: `receive_deferred_messages`, `peek_messages` and `renew_message_lock` - `ServiceBusSession`: `get_state`, `set_state` and `renew_lock` * `azure.servicebus.exceptions.ServiceBusError` now inherits from `azure.core.exceptions.AzureError`. +* Added a `ServiceBusConnectionStringParser` which parses a connection string into a properties bag containing its component parts **Breaking Changes** From 69eb0a4ac693862973685f2c7fd81f3c88d582e6 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 27 Oct 2020 23:03:38 +0530 Subject: [PATCH 08/19] lint --- .../azure/servicebus/_common/_connection_string_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index bcea412430ab..f85c7ca122ff 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -20,7 +20,7 @@ def __init__(self, **kwargs): self._shared_access_signature = kwargs.pop('shared_access_signature', None) self._shared_access_key_name = kwargs.pop('shared_access_key_name', None) self._shared_access_key = kwargs.pop('shared_access_key', None) - + @property def fully_qualified_namespace(self): """The fully qualified host name for the Service Bus namespace. From cc21f02dca2f9293ee7016e78ab250e7044f7dfd Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Wed, 28 Oct 2020 19:45:45 +0530 Subject: [PATCH 09/19] change to method --- sdk/servicebus/azure-servicebus/CHANGELOG.md | 2 + .../azure/servicebus/__init__.py | 4 +- .../_common/_connection_string_parser.py | 74 ++++++++----------- .../sync_samples/connection_string_parser.py | 4 +- .../tests/test_connection_string_parser.py | 12 +-- 5 files changed, 44 insertions(+), 52 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index 55717fb6e261..82b296face51 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -10,6 +10,8 @@ - `ServiceBusSession`: `get_state`, `set_state` and `renew_lock` * `azure.servicebus.exceptions.ServiceBusError` now inherits from `azure.core.exceptions.AzureError`. * Added a `ServiceBusConnectionStringParser` which parses a connection string into a properties bag containing its component parts + - `ReceivedMessage`: `renew_lock` +* Added a `parse_connection_string` method which parses a connection string into a properties bag containing its component parts **Breaking Changes** diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py b/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py index 9cb84bae570e..f14fdd0babf4 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py @@ -16,7 +16,7 @@ from ._common.constants import ReceiveMode, SubQueue, NEXT_AVAILABLE_SESSION from ._common.auto_lock_renewer import AutoLockRenewer from ._common._connection_string_parser import ( - ServiceBusConnectionStringParser, + parse_connection_string, ServiceBusConnectionStringProperties ) @@ -35,6 +35,6 @@ 'ServiceBusSender', 'TransportType', 'AutoLockRenewer', - 'ServiceBusConnectionStringParser', + 'parse_connection_string', 'ServiceBusConnectionStringProperties' ] diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index f85c7ca122ff..9e871906fcf5 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -62,49 +62,39 @@ def shared_access_key(self): """ return self._shared_access_key -class ServiceBusConnectionStringParser(object): + + +def parse_connection_string(conn_str): + # type(str) -> ServiceBusConnectionStringProperties """Parse the connection string. :param conn_str: The connection string that has to be parsed. + :type conn_str: str """ - def __init__(self, conn_str): - # type: (str) -> None - """ - :param conn_str: The connection string to parse. - :type conn_str: str - """ - self._conn_str = conn_str - - def parse(self): - # type() -> ServiceBusConnectionStringProperties - """ - Parse the connection string. - """ - conn_str = self._conn_str.rstrip(";") - conn_settings = [s.split("=", 1) for s in conn_str.split(";")] - if any(len(tup) != 2 for tup in conn_settings): - raise ValueError("Connection string is either blank or malformed.") - conn_settings = dict(conn_settings) - shared_access_signature = None - for key, value in conn_settings.items(): - if key.lower() == 'sharedaccesssignature': - shared_access_signature = value - shared_access_key = conn_settings.get('SharedAccessKey') - if shared_access_signature is not None and shared_access_key is not None: - raise ValueError("Only one of the SharedAccessKey or SharedAccessSignature must be present.") - endpoint = conn_settings.get('Endpoint') - if not endpoint: - raise ValueError("Connection string is either blank or malformed.") - parsed = urlparse(endpoint.rstrip('/')) - if not parsed.netloc: - raise ValueError("Invalid Endpoint on the Connection String.") - namespace = parsed.netloc.strip() - props = { - 'fully_qualified_namespace': namespace, - 'endpoint': endpoint, - 'entity_path': conn_settings.get('EntityPath'), - 'shared_access_signature': shared_access_signature, - 'shared_access_key_name': conn_settings.get('SharedAccessKeyName'), - 'shared_access_key': shared_access_key - } - return ServiceBusConnectionStringProperties(**props) + conn_settings = [s.split("=", 1) for s in conn_str.split(";")] + if any(len(tup) != 2 for tup in conn_settings): + raise ValueError("Connection string is either blank or malformed.") + conn_settings = dict(conn_settings) + shared_access_signature = None + for key, value in conn_settings.items(): + if key.lower() == 'sharedaccesssignature': + shared_access_signature = value + shared_access_key = conn_settings.get('SharedAccessKey') + if shared_access_signature is not None and shared_access_key is not None: + raise ValueError("Only one of the SharedAccessKey or SharedAccessSignature must be present.") + endpoint = conn_settings.get('Endpoint') + if not endpoint: + raise ValueError("Connection string is either blank or malformed.") + parsed = urlparse(endpoint.rstrip('/')) + if not parsed.netloc: + raise ValueError("Invalid Endpoint on the Connection String.") + namespace = parsed.netloc.strip() + props = { + 'fully_qualified_namespace': namespace, + 'endpoint': endpoint, + 'entity_path': conn_settings.get('EntityPath'), + 'shared_access_signature': shared_access_signature, + 'shared_access_key_name': conn_settings.get('SharedAccessKeyName'), + 'shared_access_key': shared_access_key + } + return ServiceBusConnectionStringProperties(**props) diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py index c7dbdb70c4e3..6ce1a84d6d73 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py @@ -13,7 +13,7 @@ ServiceBusClient, Message, ServiceBusConnectionStringProperties, - ServiceBusConnectionStringParser, + parse_connection_string, ) from azure.servicebus._base_handler import ServiceBusSharedKeyCredential @@ -21,7 +21,7 @@ conn_str = os.environ['SERVICE_BUS_CONN_STR'] QUEUE_NAME = os.environ["SERVICE_BUS_QUEUE_NAME"] -parse_result = ServiceBusConnectionStringParser(conn_str).parse() +parse_result = parse_connection_string(conn_str) fully_qualified_namespace = parse_result.fully_qualified_namespace credential = ServiceBusSharedKeyCredential(parse_result.shared_access_key_name, parse_result.shared_access_key) diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py index 67662252982b..d4cdd9f0ca6f 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -10,7 +10,7 @@ ServiceBusClient, Message, ServiceBusConnectionStringProperties, - ServiceBusConnectionStringParser, + parse_connection_string, ) from devtools_testutils import AzureMgmtTestCase @@ -19,7 +19,7 @@ class ServiceBusConnectionStringParserTests(AzureMgmtTestCase): def test_sb_conn_str_parse_cs(self, **kwargs): conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' - parse_result = ServiceBusConnectionStringParser(conn_str).parse() + parse_result = parse_connection_string(conn_str) assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' assert parse_result.shared_access_key_name == 'test' @@ -28,24 +28,24 @@ def test_sb_conn_str_parse_cs(self, **kwargs): def test_sb_conn_str_parse_sas_and_shared_key(self, **kwargs): conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=;SharedAccessSignature=THISISASASXXXXXXX=' with pytest.raises(ValueError) as e: - parse_result = ServiceBusConnectionStringParser(conn_str).parse() + parse_result = parse_connection_string(conn_str) assert str(e.value) == 'Only one of the SharedAccessKey or SharedAccessSignature must be present.' def test_sb_parse_malformed_conn_str_no_endpoint(self, **kwargs): conn_str = 'SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' with pytest.raises(ValueError) as e: - parse_result = ServiceBusConnectionStringParser(conn_str).parse() + parse_result = parse_connection_string(conn_str) assert str(e.value) == 'Connection string is either blank or malformed.' def test_sb_parse_malformed_conn_str_no_netloc(self, **kwargs): conn_str = 'Endpoint=MALFORMED;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' with pytest.raises(ValueError) as e: - parse_result = ServiceBusConnectionStringParser(conn_str).parse() + parse_result = parse_connection_string(conn_str) assert str(e.value) == 'Invalid Endpoint on the Connection String.' def test_sb_parse_conn_str_sas(self, **kwargs): conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessSignature=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' - parse_result = ServiceBusConnectionStringParser(conn_str).parse() + parse_result = parse_connection_string(conn_str) assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' assert parse_result.shared_access_signature == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' From de63a685700c7fa415632d45bfbd3bae3401c53a Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Thu, 29 Oct 2020 20:07:55 +0530 Subject: [PATCH 10/19] hide shared access key --- .../_common/_connection_string_parser.py | 4 ++-- .../sync_samples/connection_string_parser.py | 17 ++++------------- .../tests/test_connection_string_parser.py | 1 - 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 9e871906fcf5..631eb6ce95ed 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -48,14 +48,14 @@ def shared_access_signature(self): return self._shared_access_signature @property - def shared_access_key_name(self): + def _shared_access_key_name(self): """ The name of the shared access key. """ return self._shared_access_key_name @property - def shared_access_key(self): + def _shared_access_key(self): """ Required for authentication along with the key name. A shared_access_signature can be used alternatively. diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py index 6ce1a84d6d73..7a991c734a9d 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py @@ -16,31 +16,22 @@ parse_connection_string, ) -from azure.servicebus._base_handler import ServiceBusSharedKeyCredential - conn_str = os.environ['SERVICE_BUS_CONN_STR'] QUEUE_NAME = os.environ["SERVICE_BUS_QUEUE_NAME"] parse_result = parse_connection_string(conn_str) fully_qualified_namespace = parse_result.fully_qualified_namespace -credential = ServiceBusSharedKeyCredential(parse_result.shared_access_key_name, parse_result.shared_access_key) - -# initialize with credential and namespace from the parse_result. -servicebus_client = ServiceBusClient(fully_qualified_namespace, credential) -with servicebus_client: - sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) - with sender: - sender.send_messages(Message('Single Message')) - -print("Send message is done.") +print(fully_qualified_namespace) +# the name of the queue/topic +entity_path = parse_result.entity_path # initialize with DefaultAzureCredential servicebus_client = ServiceBusClient( fully_qualified_namespace, DefaultAzureCredential(), - parse_result.entity_path + entity_path ) with servicebus_client: sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py index d4cdd9f0ca6f..4b2cb8dd315c 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -14,7 +14,6 @@ ) from devtools_testutils import AzureMgmtTestCase -from azure.servicebus._base_handler import ServiceBusSharedKeyCredential class ServiceBusConnectionStringParserTests(AzureMgmtTestCase): def test_sb_conn_str_parse_cs(self, **kwargs): From dda76fbfa13d1db4074ad935a9141203328fc407 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Thu, 29 Oct 2020 21:12:59 +0530 Subject: [PATCH 11/19] fix --- .../_common/_connection_string_parser.py | 16 ---------------- .../sync_samples/connection_string_parser.py | 4 ++-- .../tests/test_connection_string_parser.py | 8 +++----- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 631eb6ce95ed..7893dab48980 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -47,22 +47,6 @@ def shared_access_signature(self): """ return self._shared_access_signature - @property - def _shared_access_key_name(self): - """ - The name of the shared access key. - """ - return self._shared_access_key_name - - @property - def _shared_access_key(self): - """ - Required for authentication along with the key name. A shared_access_signature can be used - alternatively. - """ - return self._shared_access_key - - def parse_connection_string(conn_str): # type(str) -> ServiceBusConnectionStringProperties diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py index 7a991c734a9d..fb1ba9f4a27c 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py @@ -11,7 +11,7 @@ import os from azure.servicebus import ( ServiceBusClient, - Message, + ServiceBusMessage, ServiceBusConnectionStringProperties, parse_connection_string, ) @@ -36,6 +36,6 @@ with servicebus_client: sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) with sender: - sender.send_messages(Message('Single Message')) + sender.send_messages(ServiceBusMessage('Single Message')) print("Send message is done.") diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py index 4b2cb8dd315c..0c09cb04cb53 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -7,8 +7,6 @@ import os import pytest from azure.servicebus import ( - ServiceBusClient, - Message, ServiceBusConnectionStringProperties, parse_connection_string, ) @@ -21,8 +19,8 @@ def test_sb_conn_str_parse_cs(self, **kwargs): parse_result = parse_connection_string(conn_str) assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' - assert parse_result.shared_access_key_name == 'test' - assert parse_result.shared_access_key == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + assert parse_result._shared_access_key_name == 'test' + assert parse_result._shared_access_key == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' def test_sb_conn_str_parse_sas_and_shared_key(self, **kwargs): conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=;SharedAccessSignature=THISISASASXXXXXXX=' @@ -48,4 +46,4 @@ def test_sb_parse_conn_str_sas(self, **kwargs): assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' assert parse_result.shared_access_signature == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' - assert parse_result.shared_access_key_name == None + assert parse_result._shared_access_key_name == None From c8ae3dd87aa30f51f8be62e4f62f6d71cfed0a36 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 2 Nov 2020 09:56:30 +0530 Subject: [PATCH 12/19] Apply suggestions from code review Co-authored-by: Adam Ling (MSFT) --- .../azure/servicebus/_common/_connection_string_parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 7893dab48980..aa70f59a9f02 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -50,10 +50,11 @@ def shared_access_signature(self): def parse_connection_string(conn_str): # type(str) -> ServiceBusConnectionStringProperties - """Parse the connection string. + """Parse the connection string into a properties bag containing its component parts. :param conn_str: The connection string that has to be parsed. :type conn_str: str + :rtype: ~azure.servicebus.ServiceBusConnectionStringProperties """ conn_settings = [s.split("=", 1) for s in conn_str.split(";")] if any(len(tup) != 2 for tup in conn_settings): From 3dbd773645d7d68f811102f21f47183f49a7eb26 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 2 Nov 2020 10:11:28 +0530 Subject: [PATCH 13/19] coments --- .../_common/_connection_string_parser.py | 8 ++-- .../sync_samples/connection_string_parser.py | 41 ------------------- 2 files changed, 4 insertions(+), 45 deletions(-) delete mode 100644 sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index aa70f59a9f02..21ee2b7d9a42 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -16,7 +16,7 @@ class ServiceBusConnectionStringProperties(DictMixin): def __init__(self, **kwargs): self._fully_qualified_namespace = kwargs.pop('fully_qualified_namespace', None) self._endpoint = kwargs.pop('endpoint', None) - self._entity_path = kwargs.pop('entity_path', None) + self._entity_name = kwargs.pop('entity_name', None) self._shared_access_signature = kwargs.pop('shared_access_signature', None) self._shared_access_key_name = kwargs.pop('shared_access_key_name', None) self._shared_access_key = kwargs.pop('shared_access_key', None) @@ -35,10 +35,10 @@ def endpoint(self): return self._endpoint @property - def entity_path(self): + def entity_name(self): """Optional. Represents the name of the queue/topic. """ - return self._entity_path + return self._entity_name @property def shared_access_signature(self): @@ -77,7 +77,7 @@ def parse_connection_string(conn_str): props = { 'fully_qualified_namespace': namespace, 'endpoint': endpoint, - 'entity_path': conn_settings.get('EntityPath'), + 'entity_name': conn_settings.get('EntityPath'), 'shared_access_signature': shared_access_signature, 'shared_access_key_name': conn_settings.get('SharedAccessKeyName'), 'shared_access_key': shared_access_key diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py deleted file mode 100644 index fb1ba9f4a27c..000000000000 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/connection_string_parser.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - -# -------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------------------------- -""" -An example to show the usage of connection string parser. -""" - -import os -from azure.servicebus import ( - ServiceBusClient, - ServiceBusMessage, - ServiceBusConnectionStringProperties, - parse_connection_string, -) - -conn_str = os.environ['SERVICE_BUS_CONN_STR'] -QUEUE_NAME = os.environ["SERVICE_BUS_QUEUE_NAME"] - -parse_result = parse_connection_string(conn_str) - -fully_qualified_namespace = parse_result.fully_qualified_namespace -print(fully_qualified_namespace) - -# the name of the queue/topic -entity_path = parse_result.entity_path - -# initialize with DefaultAzureCredential -servicebus_client = ServiceBusClient( - fully_qualified_namespace, - DefaultAzureCredential(), - entity_path - ) -with servicebus_client: - sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME) - with sender: - sender.send_messages(ServiceBusMessage('Single Message')) - -print("Send message is done.") From 81f3760ca37b0001404f1c027f55b8318caf43c8 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 3 Nov 2020 00:04:33 +0530 Subject: [PATCH 14/19] few more changes --- .../servicebus/_common/_connection_string_parser.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 21ee2b7d9a42..0ce062916ad5 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -16,7 +16,7 @@ class ServiceBusConnectionStringProperties(DictMixin): def __init__(self, **kwargs): self._fully_qualified_namespace = kwargs.pop('fully_qualified_namespace', None) self._endpoint = kwargs.pop('endpoint', None) - self._entity_name = kwargs.pop('entity_name', None) + self._entity_path = kwargs.pop('entity_path', None) self._shared_access_signature = kwargs.pop('shared_access_signature', None) self._shared_access_key_name = kwargs.pop('shared_access_key_name', None) self._shared_access_key = kwargs.pop('shared_access_key', None) @@ -35,10 +35,10 @@ def endpoint(self): return self._endpoint @property - def entity_name(self): + def entity_path(self): """Optional. Represents the name of the queue/topic. """ - return self._entity_name + return self._entity_path @property def shared_access_signature(self): @@ -65,6 +65,9 @@ def parse_connection_string(conn_str): if key.lower() == 'sharedaccesssignature': shared_access_signature = value shared_access_key = conn_settings.get('SharedAccessKey') + shared_access_key_name = conn_settings.get('SharedAccessKeyName') + if any([shared_access_key, shared_access_key_name]) and not all([shared_access_key, shared_access_key_name]): + raise ValueError("Connection string must have both SharedAccessKeyName and SharedAccessKey.") if shared_access_signature is not None and shared_access_key is not None: raise ValueError("Only one of the SharedAccessKey or SharedAccessSignature must be present.") endpoint = conn_settings.get('Endpoint') @@ -77,9 +80,9 @@ def parse_connection_string(conn_str): props = { 'fully_qualified_namespace': namespace, 'endpoint': endpoint, - 'entity_name': conn_settings.get('EntityPath'), + 'entity_path': conn_settings.get('EntityPath'), 'shared_access_signature': shared_access_signature, - 'shared_access_key_name': conn_settings.get('SharedAccessKeyName'), + 'shared_access_key_name': shared_access_key_name, 'shared_access_key': shared_access_key } return ServiceBusConnectionStringProperties(**props) From 09f650fa668edb4635c7e9594eb1c5a8ca714cb0 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 3 Nov 2020 00:51:28 +0530 Subject: [PATCH 15/19] tests --- .../tests/test_connection_string_parser.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py index 0c09cb04cb53..7e19c84b8c78 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -47,3 +47,15 @@ def test_sb_parse_conn_str_sas(self, **kwargs): assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' assert parse_result.shared_access_signature == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' assert parse_result._shared_access_key_name == None + + def test_sb_parse_conn_str_no_keyname(self, **kwargs): + conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + with pytest.raises(ValueError) as e: + parse_result = parse_connection_string(conn_str) + assert str(e.value) == 'Connection string must have both SharedAccessKeyName and SharedAccessKey.' + + def test_sb_parse_conn_str_no_key(self, **kwargs): + conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=Test' + with pytest.raises(ValueError) as e: + parse_result = parse_connection_string(conn_str) + assert str(e.value) == 'Connection string must have both SharedAccessKeyName and SharedAccessKey.' From 225e929abb8b4dacef65a59815c5904b6411fdda Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 3 Nov 2020 09:31:07 +0530 Subject: [PATCH 16/19] add back sky --- .../_common/_connection_string_parser.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py index 0ce062916ad5..5c9d566c6327 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/_connection_string_parser.py @@ -47,6 +47,20 @@ def shared_access_signature(self): """ return self._shared_access_signature + @property + def shared_access_key_name(self): + """ + The name of the shared_access_key. This must be used along with the shared_access_key. + """ + return self._shared_access_key_name + + @property + def shared_access_key(self): + """ + The shared_access_key can be used along with the shared_access_key_name as a credential. + """ + return self._shared_access_key + def parse_connection_string(conn_str): # type(str) -> ServiceBusConnectionStringProperties From 0d6481235b6682dd9473cc8ad8f1def692808696 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 3 Nov 2020 09:39:05 +0530 Subject: [PATCH 17/19] test --- .../azure-servicebus/tests/test_connection_string_parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py index 7e19c84b8c78..fd0657006f54 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py +++ b/sdk/servicebus/azure-servicebus/tests/test_connection_string_parser.py @@ -19,8 +19,8 @@ def test_sb_conn_str_parse_cs(self, **kwargs): parse_result = parse_connection_string(conn_str) assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' - assert parse_result._shared_access_key_name == 'test' - assert parse_result._shared_access_key == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' + assert parse_result.shared_access_key_name == 'test' + assert parse_result.shared_access_key == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' def test_sb_conn_str_parse_sas_and_shared_key(self, **kwargs): conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=;SharedAccessSignature=THISISASASXXXXXXX=' @@ -46,7 +46,7 @@ def test_sb_parse_conn_str_sas(self, **kwargs): assert parse_result.endpoint == 'sb://resourcename.servicebus.windows.net/' assert parse_result.fully_qualified_namespace == 'resourcename.servicebus.windows.net' assert parse_result.shared_access_signature == 'THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' - assert parse_result._shared_access_key_name == None + assert parse_result.shared_access_key_name == None def test_sb_parse_conn_str_no_keyname(self, **kwargs): conn_str = 'Endpoint=sb://resourcename.servicebus.windows.net/;SharedAccessKey=THISISATESTKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXX=' From b188f5fb80a53cfdd498c829817e6e56d941329a Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 3 Nov 2020 09:45:41 +0530 Subject: [PATCH 18/19] changelog --- sdk/servicebus/azure-servicebus/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index 82b296face51..f37f1e670378 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -9,8 +9,6 @@ - `ServiceBusReceiver`: `receive_deferred_messages`, `peek_messages` and `renew_message_lock` - `ServiceBusSession`: `get_state`, `set_state` and `renew_lock` * `azure.servicebus.exceptions.ServiceBusError` now inherits from `azure.core.exceptions.AzureError`. -* Added a `ServiceBusConnectionStringParser` which parses a connection string into a properties bag containing its component parts - - `ReceivedMessage`: `renew_lock` * Added a `parse_connection_string` method which parses a connection string into a properties bag containing its component parts **Breaking Changes** From 282abc6c916df7cb4c4868364c970f534d8d35bf Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 3 Nov 2020 09:47:01 +0530 Subject: [PATCH 19/19] Update sdk/servicebus/azure-servicebus/CHANGELOG.md --- sdk/servicebus/azure-servicebus/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index f37f1e670378..0e7cc22991c3 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -47,8 +47,6 @@ now raise more concrete exception other than `MessageSettleFailed` and `ServiceB * `AutoLockRenewer.register` now takes `ServiceBusReceiver` as a positional parameter. * Removed `encoding` support from `ServiceBusMessage`. -* Added a `ServiceBusConnectionStringParser` which parses a connection string into individual entities. - **BugFixes** * Updated uAMQP dependency to 1.2.12.