From ee6ca808a1a9aeb100f767157747a28d68bbe61b Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 19 Mar 2021 10:27:11 -0700 Subject: [PATCH 1/5] Updated async credentials --- .../chat/_shared/user_credential.py | 15 ++---- .../chat/_shared/user_credential_async.py | 46 ++++++++++--------- .../azure/communication/chat/_shared/utils.py | 6 +++ .../chat/aio/_chat_client_async.py | 4 +- .../chat/aio/_chat_thread_client_async.py | 4 +- 5 files changed, 39 insertions(+), 36 deletions(-) diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py index dd68c96b8737..3de33e5827ec 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py @@ -10,8 +10,7 @@ Tuple, ) -from msrest.serialization import TZ_UTC - +from .utils import get_current_utc_with_tz from .user_token_refresh_options import CommunicationTokenRefreshOptions class CommunicationTokenCredential(object): @@ -35,8 +34,8 @@ def __init__(self, self._lock = Condition(Lock()) self._some_thread_refreshing = False - def get_token(self): - # type () -> ~azure.core.credentials.AccessToken + def get_token(self, *scopes, **kwargs): + # type (*str, **Any) -> AccessToken """The value of the configured token. :rtype: ~azure.core.credentials.AccessToken """ @@ -79,12 +78,8 @@ def _wait_till_inprogress_thread_finish_refreshing(self): self._lock.acquire() def _token_expiring(self): - return self._token.expires_on - self._get_utc_now() <\ + return self._token.expires_on - get_current_utc_with_tz() <\ timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES) def _is_currenttoken_valid(self): - return self._get_utc_now() < self._token.expires_on - - @classmethod - def _get_utc_now(cls): - return datetime.now().replace(tzinfo=TZ_UTC) + return get_current_utc_with_tz() < self._token.expires_on diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py index b49c593a066d..55ce843dcc70 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py @@ -8,25 +8,23 @@ from typing import ( # pylint: disable=unused-import cast, Tuple, + Any ) -from msrest.serialization import TZ_UTC - +from .utils import get_current_utc_with_tz from .user_token_refresh_options import CommunicationTokenRefreshOptions + class CommunicationTokenCredential(object): """Credential type used for authenticating to an Azure Communication service. :param str token: The token used to authenticate to an Azure Communication service - :keyword token_refresher: The token refresher to provide capacity to fetch fresh token + :keyword token_refresher: The asynctoken refresher to provide capacity to fetch fresh token :raises: TypeError """ _ON_DEMAND_REFRESHING_INTERVAL_MINUTES = 2 - def __init__(self, - token, # type: str - **kwargs - ): + def __init__(self, token: str, **kwargs: Any): token_refresher = kwargs.pop('token_refresher', None) communication_token_refresh_options = CommunicationTokenRefreshOptions(token=token, token_refresher=token_refresher) @@ -35,25 +33,24 @@ def __init__(self, self._lock = Condition(Lock()) self._some_thread_refreshing = False - def get_token(self): - # type () -> ~azure.core.credentials.AccessToken + async def get_token(self, *scopes, **kwargs): + # type (*str, **Any) -> AccessToken """The value of the configured token. :rtype: ~azure.core.credentials.AccessToken """ - if not self._token_refresher or not self._token_expiring(): return self._token should_this_thread_refresh = False - with self._lock: + async with self._lock: while self._token_expiring(): if self._some_thread_refreshing: if self._is_currenttoken_valid(): return self._token - self._wait_till_inprogress_thread_finish_refreshing() + await self._wait_till_inprogress_thread_finish_refreshing() else: should_this_thread_refresh = True self._some_thread_refreshing = True @@ -62,14 +59,14 @@ def get_token(self): if should_this_thread_refresh: try: - newtoken = self._token_refresher() # pylint:disable=not-callable + newtoken = await self._token_refresher() # pylint:disable=not-callable - with self._lock: + async with self._lock: self._token = newtoken self._some_thread_refreshing = False self._lock.notify_all() except: - with self._lock: + async with self._lock: self._some_thread_refreshing = False self._lock.notify_all() @@ -77,17 +74,22 @@ def get_token(self): return self._token - def _wait_till_inprogress_thread_finish_refreshing(self): + async def _wait_till_inprogress_thread_finish_refreshing(self): self._lock.release() - self._lock.acquire() + await self._lock.acquire() def _token_expiring(self): - return self._token.expires_on - self._get_utc_now() <\ + return self._token.expires_on - get_current_utc_with_tz() <\ timedelta(minutes=self._ON_DEMAND_REFRESHING_INTERVAL_MINUTES) def _is_currenttoken_valid(self): - return self._get_utc_now() < self._token.expires_on + return get_current_utc_with_tz() < self._token.expires_on + + async def close(self) -> None: + pass + + async def __aenter__(self): + return self - @classmethod - def _get_utc_now(cls): - return datetime.now().replace(tzinfo=TZ_UTC) + async def __aexit__(self, *args): + await self.close() diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py index 12f338c8dbd7..e138abffe37d 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py @@ -43,6 +43,12 @@ def get_current_utc_time(): # type: () -> str return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" + +def get_current_utc_with_tz(): + # type: () -> datetime + return datetime.now().replace(tzinfo=TZ_UTC) + + def create_access_token(token): # type: (str) -> azure.core.credentials.AccessToken """Creates an instance of azure.core.credentials.AccessToken from a diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_client_async.py b/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_client_async.py index aba5a8a18cd2..3879f67abea3 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_client_async.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_client_async.py @@ -16,7 +16,7 @@ import six from azure.core.tracing.decorator import distributed_trace from azure.core.tracing.decorator_async import distributed_trace_async -from azure.core.pipeline.policies import BearerTokenCredentialPolicy +from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy from azure.core.exceptions import HttpResponseError from azure.core.async_paging import AsyncItemPaged @@ -86,7 +86,7 @@ def __init__( self._client = AzureCommunicationChatService( self._endpoint, - authentication_policy=BearerTokenCredentialPolicy(self._credential), + authentication_policy=AsyncBearerTokenCredentialPolicy(self._credential), sdk_moniker=SDK_MONIKER, **kwargs) diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_thread_client_async.py b/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_thread_client_async.py index 758f996d7e71..f2388f972185 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_thread_client_async.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/aio/_chat_thread_client_async.py @@ -15,7 +15,7 @@ import six from azure.core.tracing.decorator import distributed_trace from azure.core.tracing.decorator_async import distributed_trace_async -from azure.core.pipeline.policies import BearerTokenCredentialPolicy +from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy from azure.core.async_paging import AsyncItemPaged from .._shared.user_credential_async import CommunicationTokenCredential @@ -103,7 +103,7 @@ def __init__( self._client = AzureCommunicationChatService( endpoint, - authentication_policy=BearerTokenCredentialPolicy(self._credential), + authentication_policy=AsyncBearerTokenCredentialPolicy(self._credential), sdk_moniker=SDK_MONIKER, **kwargs) From 5bf6597ff6e14886ba1a3dcf40c02b04548b2e79 Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 19 Mar 2021 11:21:07 -0700 Subject: [PATCH 2/5] Fix CI --- .../azure/communication/chat/_shared/user_credential.py | 2 +- .../communication/chat/_shared/user_credential_async.py | 6 +++--- .../tests/test_chat_client_async.py | 4 ++-- .../tests/test_chat_thread_client_async.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py index 3de33e5827ec..94671decb8ee 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py @@ -34,7 +34,7 @@ def __init__(self, self._lock = Condition(Lock()) self._some_thread_refreshing = False - def get_token(self, *scopes, **kwargs): + def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument # type (*str, **Any) -> AccessToken """The value of the configured token. :rtype: ~azure.core.credentials.AccessToken diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py index 55ce843dcc70..5be2fafd19da 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py @@ -4,7 +4,7 @@ # license information. # -------------------------------------------------------------------------- from asyncio import Condition, Lock -from datetime import datetime, timedelta +from datetime import timedelta from typing import ( # pylint: disable=unused-import cast, Tuple, @@ -33,7 +33,7 @@ def __init__(self, token: str, **kwargs: Any): self._lock = Condition(Lock()) self._some_thread_refreshing = False - async def get_token(self, *scopes, **kwargs): + async def get_token(self, *scopes, **kwargs): # pylint: disable=unused-argument # type (*str, **Any) -> AccessToken """The value of the configured token. :rtype: ~azure.core.credentials.AccessToken @@ -84,7 +84,7 @@ def _token_expiring(self): def _is_currenttoken_valid(self): return get_current_utc_with_tz() < self._token.expires_on - + async def close(self) -> None: pass diff --git a/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py b/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py index 9855337d5db3..911c5ebb1b43 100644 --- a/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py +++ b/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py @@ -18,14 +18,14 @@ from msrest.serialization import TZ_UTC try: - from unittest.mock import Mock, patch + from unittest.mock import Mock, AsyncMock, patch except ImportError: # python < 3.3 from mock import Mock, patch # type: ignore import pytest credential = Mock() -credential.get_token = Mock(return_value=AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC))) +credential.get_token = AsyncMock(return_value=AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC))) @pytest.mark.asyncio async def test_create_chat_thread(): diff --git a/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py b/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py index ea1d7424529d..434d8d09b684 100644 --- a/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py +++ b/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py @@ -18,14 +18,14 @@ from azure.core.exceptions import HttpResponseError try: - from unittest.mock import Mock, patch + from unittest.mock import Mock, AsyncMock, patch except ImportError: # python < 3.3 from mock import Mock, patch # type: ignore import pytest credential = Mock() -credential.get_token = Mock(return_value=AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC))) +credential.get_token = AsyncMock(return_value=AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC))) @pytest.mark.asyncio async def test_update_topic(): From 6ff7be0513603db8afa963898111ba51ac18dcd0 Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 19 Mar 2021 12:34:56 -0700 Subject: [PATCH 3/5] Fix tests --- .../communication/chat/_shared/user_credential.py | 2 +- .../tests/test_chat_client_async.py | 10 +++++++--- .../tests/test_chat_thread_client_async.py | 10 +++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py index 94671decb8ee..9498a53583ac 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential.py @@ -4,7 +4,7 @@ # license information. # -------------------------------------------------------------------------- from threading import Lock, Condition -from datetime import datetime, timedelta +from datetime import timedelta from typing import ( # pylint: disable=unused-import cast, Tuple, diff --git a/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py b/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py index 911c5ebb1b43..3b6f0d52e4de 100644 --- a/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py +++ b/sdk/communication/azure-communication-chat/tests/test_chat_client_async.py @@ -18,14 +18,18 @@ from msrest.serialization import TZ_UTC try: - from unittest.mock import Mock, AsyncMock, patch + from unittest.mock import Mock, patch except ImportError: # python < 3.3 from mock import Mock, patch # type: ignore import pytest -credential = Mock() -credential.get_token = AsyncMock(return_value=AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC))) + +async def mock_get_token(): + return AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC)) + +credential = Mock(get_token=mock_get_token) + @pytest.mark.asyncio async def test_create_chat_thread(): diff --git a/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py b/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py index 434d8d09b684..23acdb93960a 100644 --- a/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py +++ b/sdk/communication/azure-communication-chat/tests/test_chat_thread_client_async.py @@ -18,14 +18,18 @@ from azure.core.exceptions import HttpResponseError try: - from unittest.mock import Mock, AsyncMock, patch + from unittest.mock import Mock, patch except ImportError: # python < 3.3 from mock import Mock, patch # type: ignore import pytest -credential = Mock() -credential.get_token = AsyncMock(return_value=AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC))) + +async def mock_get_token(): + return AccessToken("some_token", datetime.now().replace(tzinfo=TZ_UTC)) + +credential = Mock(get_token=mock_get_token) + @pytest.mark.asyncio async def test_update_topic(): From d81ef3c5ec82f65bf0ae5cebce7e04bed64e4f92 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 23 Mar 2021 07:49:25 -0700 Subject: [PATCH 4/5] Updated utils --- .../azure/communication/chat/_shared/utils.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py index 50a3bd3cd39f..3ce828fa0350 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py @@ -15,6 +15,12 @@ from msrest.serialization import TZ_UTC from azure.core.credentials import AccessToken + +def _convert_datetime_to_utc_int(expires_on): + epoch = time.mktime(datetime(1970, 1, 1).timetuple()) + return epoch-time.mktime(expires_on.timetuple()) + + def parse_connection_str(conn_str): # type: (str) -> Tuple[str, str, str, str] endpoint = None @@ -44,9 +50,11 @@ def get_current_utc_time(): return str(datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S ")) + "GMT" -def get_current_utc_with_tz(): - # type: () -> datetime - return datetime.now().replace(tzinfo=TZ_UTC) +def get_current_utc_as_int(): + # type: () -> int + current_utc_datetime = datetime.utcnow().replace(tzinfo=TZ_UTC) + return _convert_datetime_to_utc_int(current_utc_datetime) + return current_utc_datetime_as_int def create_access_token(token): @@ -77,6 +85,7 @@ def create_access_token(token): except ValueError: raise ValueError(token_parse_err_msg) + def get_authentication_policy( endpoint, # type: str credential, # type: TokenCredential or str @@ -107,7 +116,3 @@ def get_authentication_policy( raise TypeError("Unsupported credential: {}. Use an access token string to use HMACCredentialsPolicy" "or a token credential from azure.identity".format(type(credential))) - -def _convert_datetime_to_utc_int(expires_on): - epoch = time.mktime(datetime(1970, 1, 1).timetuple()) - return epoch-time.mktime(expires_on.timetuple()) From 2f3910dade476e395cb37f37a27155c6ad685f12 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 23 Mar 2021 08:33:21 -0700 Subject: [PATCH 5/5] Pylint fix --- .../azure/communication/chat/_shared/user_credential_async.py | 2 +- .../azure/communication/chat/_shared/utils.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py index 6a5199680860..52a99e7a4b6a 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/user_credential_async.py @@ -18,7 +18,7 @@ class CommunicationTokenCredential(object): """Credential type used for authenticating to an Azure Communication service. :param str token: The token used to authenticate to an Azure Communication service - :keyword token_refresher: The asynctoken refresher to provide capacity to fetch fresh token + :keyword token_refresher: The async token refresher to provide capacity to fetch fresh token :raises: TypeError """ diff --git a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py index 3ce828fa0350..f7f4046b0b74 100644 --- a/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py +++ b/sdk/communication/azure-communication-chat/azure/communication/chat/_shared/utils.py @@ -54,7 +54,6 @@ def get_current_utc_as_int(): # type: () -> int current_utc_datetime = datetime.utcnow().replace(tzinfo=TZ_UTC) return _convert_datetime_to_utc_int(current_utc_datetime) - return current_utc_datetime_as_int def create_access_token(token):