diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/__init__.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/__init__.py index 9aa08ba587b6..2881759b4224 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/__init__.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/__init__.py @@ -23,6 +23,7 @@ CallingServerEventType ) from ._shared.models import CommunicationIdentifier, CommunicationUserIdentifier, PhoneNumberIdentifier +from ._download import ContentStreamDownloader __all__ = [ 'AudioRoutingMode', @@ -35,6 +36,7 @@ 'CommunicationIdentifier', 'CommunicationUserIdentifier', 'CallingEventSubscriptionType', + 'ContentStreamDownloader', 'CallMediaType', 'CallingOperationStatus', 'PhoneNumberIdentifier', diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_callingserver_client.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_callingserver_client.py index cdccfb9aeaa1..fd284e461710 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_callingserver_client.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_callingserver_client.py @@ -696,9 +696,9 @@ def start_recording( # pylint: disable=too-many-arguments start_call_recording_with_calllocator_request = StartCallRecordingWithCallLocatorRequest( call_locator=serialize_call_locator(call_locator), recording_state_callback_uri=recording_state_callback_uri, - recording_content_type=recording_content_type, - recording_channel_type=recording_channel_type, - recording_format_type=recording_format_type, + recording_content_type=kwargs.pop("content_type", None), + recording_channel_type=kwargs.pop("channel_type", None), + recording_format_type=kwargs.pop("format_type", None), **kwargs ) @@ -712,13 +712,11 @@ def pause_recording( self, recording_id, # type: str **kwargs # type: Any - ): # type: (...) -> HttpResponse + ): # type: (...) -> None """Pause recording the call. :param recording_id: Required. The recording id. :type recording_id: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse :raises: ~azure.core.exceptions.HttpResponseError """ @@ -732,13 +730,11 @@ def resume_recording( self, recording_id, # type: str **kwargs # type: Any - ): # type: (...) -> HttpResponse + ): # type: (...) -> None """Resume recording the call. :param recording_id: Required. The recording id. :type recording_id: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse :raises: ~azure.core.exceptions.HttpResponseError """ @@ -752,13 +748,11 @@ def stop_recording( self, recording_id, # type: str **kwargs # type: Any - ): # type: (...) -> HttpResponse + ): # type: (...) -> None """Stop recording the call. :param recording_id: Required. The recording id. :type recording_id: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse :raises: ~azure.core.exceptions.HttpResponseError """ @@ -768,7 +762,7 @@ def stop_recording( ) @distributed_trace() - def get_recording_properities( + def get_recording_properties( self, recording_id, # type: str **kwargs # type: Any @@ -819,12 +813,9 @@ def download( self._callingserver_service_client._config) return ContentStreamDownloader( + content_url, content_downloader, self._callingserver_service_client._config, - start_range, - end_range, - endpoint=content_url, - parallel_download_options=parallel_download_options, **kwargs ) @@ -833,14 +824,11 @@ def delete_recording( self, content_delete_url, # type: str **kwargs # type: Any - ): # type: (...) -> HttpResponse - """Delete recording. + ): # type: (...) -> None + """Deletes the recording and all its related content. :param content_delete_url: Required. The content delete url. :type content_delete_url: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse - :raises: ~azure.core.exceptions.HttpResponseError """ # pylint: disable=protected-access @@ -868,5 +856,3 @@ def delete_recording( map_error(status_code=response.status_code, response=response, error_map=error_map) raise HttpResponseError(response=response) - - return response diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_download.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_download.py index cb594bc7d2b1..0e3d851fbf07 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_download.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_download.py @@ -171,40 +171,31 @@ class ContentStreamDownloader(): # pylint: disable=too-many-instance-attributes """A streaming object to download recording content. :ivar str endpoint: The url where the content is located. - :ivar ~azure.communication.callingserver.ContentProperties properties: - The properties of the content being downloaded. If only a range of the data is being - downloaded, this will be reflected in the properties. - :ivar int size: - The size of the total data in the stream. This will be the byte range if speficied, - otherwise the total size of the requested content. """ def __init__( self, - clients=None, - config=None, - start_range=None, - end_range=None, - endpoint=None, - parallel_download_options=None, + endpoint, + client, + config, **kwargs ): self.endpoint = endpoint self.properties = None self.size = None - self._clients = clients + self._clients = client self._config = config - self._start_range = start_range - self._end_range = end_range - self._max_concurrency = parallel_download_options.max_concurrency if parallel_download_options else 1 + self._start_range = kwargs.pop("start_range", None) + self._end_range = kwargs.pop("end_range", None) + self._max_concurrency = kwargs.pop("max_concurrency", 1) self._request_options = kwargs self._download_complete = False self._current_content = None self._file_size = None self._non_empty_ranges = None self._response = None - self._block_size = parallel_download_options.block_size if parallel_download_options else 4*1024*1024 + self._block_size = kwargs.pop("block_size", 4*1024*1024) initial_request_start = self._start_range if self._start_range is not None else 0 if self._end_range is not None and self._end_range - self._start_range < self._block_size: initial_request_end = self._end_range diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_models.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_models.py index 1aed935c94ea..88e529e5c0fb 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_models.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_models.py @@ -97,10 +97,6 @@ class CallingServerEventType(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)) class ParallelDownloadOptions(object): """The options to configure parallel downloads. - :ivar max_concurrency: Max number of threads used to download. - :type max_concurrency: int - :ivar block_size: Block size to download on each request. - :type block_size: int """ def __init__( self, diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/__init__.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/__init__.py index 28d2e0e34e4d..c07b4d821a07 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/__init__.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/__init__.py @@ -1,7 +1,8 @@ from ._callingserver_client_async import CallingServerClient from ._call_connection_async import CallConnection - +from ._download_async import ContentStreamDownloader __all__ = [ 'CallingServerClient', - 'CallConnection' + 'CallConnection', + 'ContentStreamDownloader' ] diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_callingserver_client_async.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_callingserver_client_async.py index a5323c9f149a..dd81f6649fc5 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_callingserver_client_async.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_callingserver_client_async.py @@ -674,9 +674,9 @@ async def start_recording( # pylint: disable=too-many-arguments call_locator: CallLocator, recording_state_callback_uri: str, *, - recording_content_type: Optional[RecordingContentType] = None, - recording_channel_type: Optional[RecordingChannelType] = None, - recording_format_type: Optional[RecordingFormatType] = None, + content_type: Optional[RecordingContentType] = None, + channel_type: Optional[RecordingChannelType] = None, + format_type: Optional[RecordingFormatType] = None, **kwargs: Any ) -> StartCallRecordingResult: """Start recording the call. @@ -705,9 +705,9 @@ async def start_recording( # pylint: disable=too-many-arguments start_call_recording_request = StartCallRecordingWithCallLocatorRequest( call_locator=serialize_call_locator(call_locator), recording_state_callback_uri=recording_state_callback_uri, - recording_content_type=recording_content_type, - recording_channel_type=recording_channel_type, - recording_format_type=recording_format_type, + recording_content_type=content_type, + recording_channel_type=channel_type, + recording_format_type=format_type, **kwargs ) @@ -721,16 +721,14 @@ async def pause_recording( self, recording_id: str, **kwargs: Any - ) -> HttpResponse: + ) -> None: """Pause recording the call. :param recording_id: Required. The recording id. :type recording_id: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse :raises: ~azure.core.exceptions.HttpResponseError - """ + return await self._server_call_client.pause_recording( recording_id=recording_id, **kwargs @@ -741,13 +739,11 @@ async def resume_recording( self, recording_id: str, **kwargs: Any - ) -> HttpResponse: + ) -> None: """Resume recording the call. :param recording_id: Required. The recording id. :type recording_id: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse :raises: ~azure.core.exceptions.HttpResponseError """ @@ -761,13 +757,11 @@ async def stop_recording( self, recording_id: str, **kwargs: Any - ) -> HttpResponse: + ) -> None: """Stop recording the call. :param recording_id: Required. The recording id. :type recording_id: str - :return: The response of the operation. - :rtype: ~azure.core.rest.HttpResponse :raises: ~azure.core.exceptions.HttpResponseError """ @@ -777,7 +771,7 @@ async def stop_recording( ) @distributed_trace_async() - async def get_recording_properities( + async def get_recording_properties( self, recording_id: str, **kwargs: Any @@ -800,9 +794,11 @@ async def get_recording_properities( async def download( self, content_url: str, - start_range: int = None, - end_range: int = None, - parallel_download_options: ParallelDownloadOptions = None, + *, + start_range: Optional[int] = None, + end_range: Optional[int] = None, + max_concurrency: Optional[int] = 1, + block_size: Optional[int] = 4*1024*1024, **kwargs: Any ) -> ContentStreamDownloader: """Download using content url. @@ -828,12 +824,13 @@ async def download( self._callingserver_service_client._deserialize ) stream_downloader = ContentStreamDownloader( + content_url, content_downloader, self._callingserver_service_client._config, - start_range, - end_range, - endpoint=content_url, - parallel_download_options=parallel_download_options, + start_range=start_range, + end_range=end_range, + max_concurrency=max_concurrency, + block_size=block_size, **kwargs ) await stream_downloader._setup() @@ -845,7 +842,7 @@ async def delete_recording( content_delete_url: str, **kwargs: Any - ): # type: (...) -> HttpResponse + ) -> None: """Delete recording. :param content_delete_url: Required. The content delete url. @@ -861,9 +858,9 @@ async def delete_recording( uri_to_sign_with = CallingServerUtils.get_url_to_sign_request_with(self._endpoint, content_delete_url) - query_parameters = {} # type: Dict[str, Any] + query_parameters = {} # Construct headers - header_parameters = {} # type: Dict[str, Any] + header_parameters = {} header_parameters['UriToSignWith'] = self._callingserver_service_client._serialize.header( name="uri_to_sign_with", data=uri_to_sign_with, @@ -881,8 +878,6 @@ async def delete_recording( response=response, error_map=error_map) raise HttpResponseError(response=response) - return response - async def close(self) -> None: """Close the :class: `~azure.communication.callingserver.aio.CallingServerClient` session. diff --git a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_download_async.py b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_download_async.py index 8ac5c881458e..a463a4f99567 100644 --- a/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_download_async.py +++ b/sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_download_async.py @@ -125,12 +125,14 @@ class ContentStreamDownloader(): # pylint: disable=too-many-instance-attributes """ def __init__( self, - clients=None, - config=None, + endpoint, + clients, + config, + *, start_range=None, end_range=None, - endpoint=None, - parallel_download_options=None, + max_concurrency=1, + block_size=4*1024*1024, **kwargs ): self.endpoint = endpoint @@ -141,14 +143,14 @@ def __init__( self._config = config self._start_range = start_range self._end_range = end_range - self._max_concurrency = parallel_download_options.max_concurrency if parallel_download_options else 1 + self._max_concurrency = max_concurrency self._request_options = kwargs self._download_complete = False self._current_content = None self._file_size = None self._non_empty_ranges = None self._response = None - self._block_size = parallel_download_options.block_size if parallel_download_options else 4*1024*1024 + self._block_size = block_size initial_request_start = self._start_range if self._start_range is not None else 0 if self._end_range is not None and self._end_range - self._start_range < self._block_size: initial_request_end = self._end_range diff --git a/sdk/communication/azure-communication-callingserver/dev_requirements.txt b/sdk/communication/azure-communication-callingserver/dev_requirements.txt index dc1fa5e47bba..9c173679d82e 100644 --- a/sdk/communication/azure-communication-callingserver/dev_requirements.txt +++ b/sdk/communication/azure-communication-callingserver/dev_requirements.txt @@ -5,6 +5,7 @@ -e ../../../tools/vcrpy ../../nspkg/azure-communication-nspkg ../../core/azure-core +cchardet aiohttp>=3.0; python_version >= '3.5' parameterized validators diff --git a/sdk/communication/azure-communication-callingserver/tests/recordings/test_live_download_content.test_download_content_to_stream_with_redirection.yaml b/sdk/communication/azure-communication-callingserver/tests/recordings/test_live_download_content.test_download_content_to_stream_with_redirection.yaml index 13e7cdb7e92d..6284908ca2ee 100644 --- a/sdk/communication/azure-communication-callingserver/tests/recordings/test_live_download_content.test_download_content_to_stream_with_redirection.yaml +++ b/sdk/communication/azure-communication-callingserver/tests/recordings/test_live_download_content.test_download_content_to_stream_with_redirection.yaml @@ -82,9 +82,9 @@ interactions: cache-control: - no-cache, max-age=0, s-maxage=0, private content-length: - - '1077' + - '1078' content-range: - - bytes 0-1076/1077 + - bytes 0-1077/1078 content-type: - application/octet-stream date: diff --git a/sdk/communication/azure-communication-callingserver/tests/test_live_download_content.py b/sdk/communication/azure-communication-callingserver/tests/test_live_download_content.py index 9613f3bb4d12..dfc9d1c3f21b 100644 --- a/sdk/communication/azure-communication-callingserver/tests/test_live_download_content.py +++ b/sdk/communication/azure-communication-callingserver/tests/test_live_download_content.py @@ -32,17 +32,17 @@ def test_download_content_to_stream(self): @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) def test_download_content_to_stream_on_chunks(self): - stream = self._execute_test(ParallelDownloadOptions(block_size=400)) + stream = self._execute_test({'block_size': 400}) metadata = stream.getvalue() self._verify_metadata(metadata) @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) def test_download_content_to_stream_on_chunks_parallel(self): stream = self._execute_test( - ParallelDownloadOptions( - max_concurrency=3, - block_size=400) - ) + { + 'max_concurrency': 3, + 'block_size': 400 + }) metadata = stream.getvalue() self._verify_metadata(metadata) @@ -52,9 +52,8 @@ def test_download_content_to_stream_with_redirection(self): metadata = stream.getvalue() self._verify_metadata(metadata) - def _execute_test(self, download_options=None): - downloader = self._calling_server_client.download(self._metadata_url, - parallel_download_options=download_options) + def _execute_test(self, download_options={}): + downloader = self._calling_server_client.download(self._metadata_url, **download_options) stream = BytesIO() downloader.readinto(stream) diff --git a/sdk/communication/azure-communication-callingserver/tests/test_live_download_content_async.py b/sdk/communication/azure-communication-callingserver/tests/test_live_download_content_async.py index 967f61f1bc84..bc42fe072aad 100644 --- a/sdk/communication/azure-communication-callingserver/tests/test_live_download_content_async.py +++ b/sdk/communication/azure-communication-callingserver/tests/test_live_download_content_async.py @@ -27,43 +27,48 @@ def setUp(self): @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) @AsyncCommunicationTestCase.await_prepared_test async def test_download_content_to_stream(self): - stream = await self._execute_test_async() + downloader = await self._calling_server_client.download(self._metadata_url) + stream = await self._downloadToStream(downloader) metadata = stream.getvalue() self._verify_metadata(metadata) @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) @AsyncCommunicationTestCase.await_prepared_test async def test_download_content_to_stream_on_chunks(self): - stream = await self._execute_test_async(ParallelDownloadOptions(block_size=400)) + downloader = await self._calling_server_client.download( + self._metadata_url, + block_size=400 + ) + stream = await self._downloadToStream(downloader) metadata = stream.getvalue() self._verify_metadata(metadata) @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) @AsyncCommunicationTestCase.await_prepared_test async def test_download_content_to_stream_on_chunks_parallel(self): - stream = await self._execute_test_async( - ParallelDownloadOptions( - max_concurrency=3, - block_size=100) - ) + downloader = await self._calling_server_client.download( + self._metadata_url, + max_concurrency=3, + block_size=100 + ) + stream = await self._downloadToStream(downloader) metadata = stream.getvalue() self._verify_metadata(metadata) @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) @AsyncCommunicationTestCase.await_prepared_test async def test_download_content_to_stream_with_redirection(self): - stream = await self._execute_test_async() + downloader = await self._calling_server_client.download(self._metadata_url) + stream = await self._downloadToStream(downloader) metadata = stream.getvalue() self._verify_metadata(metadata) - async def _execute_test_async(self, download_options=None): - downloader_async = await self._calling_server_client.download(self._metadata_url, - parallel_download_options=download_options) + async def _downloadToStream(self, downloader): stream = BytesIO() - - await downloader_async.readinto(stream) + await downloader.readinto(stream) return stream def _verify_metadata(self, metadata): self.assertIsNotNone(metadata) + print(metadata) self.assertTrue(metadata.__contains__(bytes(self._document_id, 'utf-8'))) diff --git a/sdk/communication/azure-communication-callingserver/tests/test_live_server_call.py b/sdk/communication/azure-communication-callingserver/tests/test_live_server_call.py index fb85fdc6d68e..85603fddaee6 100644 --- a/sdk/communication/azure-communication-callingserver/tests/test_live_server_call.py +++ b/sdk/communication/azure-communication-callingserver/tests/test_live_server_call.py @@ -169,17 +169,17 @@ def test_run_all_client_functions(self): assert recording_id is not None CallingServerLiveTestUtils.sleep_if_in_live_mode() - recording_state = self.callingserver_client.get_recording_properities(recording_id) + recording_state = self.callingserver_client.get_recording_properties(recording_id) assert recording_state.recording_state == "active" self.callingserver_client.pause_recording(recording_id) CallingServerLiveTestUtils.sleep_if_in_live_mode() - recording_state = self.callingserver_client.get_recording_properities(recording_id) + recording_state = self.callingserver_client.get_recording_properties(recording_id) assert recording_state.recording_state == "inactive" self.callingserver_client.resume_recording(recording_id) CallingServerLiveTestUtils.sleep_if_in_live_mode() - recording_state = self.callingserver_client.get_recording_properities(recording_id) + recording_state = self.callingserver_client.get_recording_properties(recording_id) assert recording_state.recording_state == "active" self.callingserver_client.stop_recording(recording_id) @@ -223,9 +223,7 @@ def test_start_recording_relative_uri_fails(self): @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) def test_delete_success(self): delete_url = CallingServerLiveTestUtils.get_delete_url() - delete_response = self.callingserver_client.delete_recording(delete_url) - assert delete_response is not None - assert delete_response.status_code == 200 + self.callingserver_client.delete_recording(delete_url) @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) def test_delete_content_not_exists(self): diff --git a/sdk/communication/azure-communication-callingserver/tests/test_live_server_call_async.py b/sdk/communication/azure-communication-callingserver/tests/test_live_server_call_async.py index 54987716fc07..45ac06eb3ebb 100644 --- a/sdk/communication/azure-communication-callingserver/tests/test_live_server_call_async.py +++ b/sdk/communication/azure-communication-callingserver/tests/test_live_server_call_async.py @@ -175,17 +175,17 @@ async def test_run_all_client_functions(self): assert recording_id is not None CallingServerLiveTestUtils.sleep_if_in_live_mode() - recording_state = await self.callingserver_client.get_recording_properities(recording_id) + recording_state = await self.callingserver_client.get_recording_properties(recording_id) assert recording_state.recording_state == "active" await self.callingserver_client.pause_recording(recording_id) CallingServerLiveTestUtils.sleep_if_in_live_mode() - recording_state = await self.callingserver_client.get_recording_properities(recording_id) + recording_state = await self.callingserver_client.get_recording_properties(recording_id) assert recording_state.recording_state == "inactive" await self.callingserver_client.resume_recording(recording_id) CallingServerLiveTestUtils.sleep_if_in_live_mode() - recording_state = await self.callingserver_client.get_recording_properities(recording_id) + recording_state = await self.callingserver_client.get_recording_properties(recording_id) assert recording_state.recording_state == "active" await self.callingserver_client.stop_recording(recording_id) @@ -206,9 +206,7 @@ async def test_start_recording_fails(self): async def test_delete_success(self): delete_url = CallingServerLiveTestUtilsAsync.get_delete_url() async with self.callingserver_client: - delete_response = await self.callingserver_client.delete_recording(delete_url) - assert delete_response is not None - assert delete_response.status_code == 200 + await self.callingserver_client.delete_recording(delete_url) @pytest.mark.skipif(CONST.SKIP_CALLINGSERVER_INTERACTION_LIVE_TESTS, reason=CONST.CALLINGSERVER_INTERACTION_LIVE_TESTS_SKIP_REASON) @AsyncCommunicationTestCase.await_prepared_test