Skip to content

Commit 02d8be6

Browse files
authored
Adding source caller id to the answer call option (#34268)
* Adding source caller id to the answer call option * Fixing the test failure on the mute participant scenario * skipping mute participant test to unblock the PR checkin Will enable it back once we have the latest test data * changing the ordering of the skip test attributes * updating the API spec endpoint
1 parent d1ee4c9 commit 02d8be6

File tree

10 files changed

+124
-25
lines changed

10 files changed

+124
-25
lines changed

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/_call_automation_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ def answer_call(
316316
operation_context: Optional[str] = None,
317317
media_streaming_configuration: Optional['MediaStreamingConfiguration'] = None,
318318
transcription_configuration: Optional['TranscriptionConfiguration'] = None,
319+
source_caller_id_number: Optional['PhoneNumberIdentifier'] = None,
319320
**kwargs
320321
) -> CallConnectionProperties:
321322
"""Answer incoming call with Azure Communication Service's IncomingCall event
@@ -336,6 +337,9 @@ def answer_call(
336337
:keyword transcription_configuration: Configuration of live transcription.
337338
:paramtype transcription_configuration: ~azure.communication.callautomation.TranscriptionConfiguration
338339
or None
340+
:source_caller_id_number: The source caller Id, a phone number, that's will be used when
341+
inviting a pstn target. Required only when transferring call to PSTN, if this is an incoming voip call.
342+
:vartype source_caller_id_number: ~azure.communication.callautomation.PhoneNumberIdentifier
339343
:return: CallConnectionProperties
340344
:rtype: ~azure.communication.callautomation.CallConnectionProperties
341345
:raises ~azure.core.exceptions.HttpResponseError:
@@ -347,6 +351,7 @@ def answer_call(
347351
answer_call_request = AnswerCallRequest(
348352
incoming_call_context=incoming_call_context,
349353
callback_uri=callback_url,
354+
source_caller_id_number=serialize_phone_identifier(source_caller_id_number),
350355
call_intelligence_options=call_intelligence_options,
351356
answered_by=serialize_communication_user_identifier(
352357
self.source) if self.source else None,

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/_generated/_serialization.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,6 @@ def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]],
170170
return None
171171

172172

173-
try:
174-
basestring # type: ignore
175-
unicode_str = unicode # type: ignore
176-
except NameError:
177-
basestring = str
178-
unicode_str = str
179-
180173
_LOGGER = logging.getLogger(__name__)
181174

182175
try:
@@ -545,7 +538,7 @@ class Serializer(object):
545538
"multiple": lambda x, y: x % y != 0,
546539
}
547540

548-
def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]] = None):
541+
def __init__(self, classes: Optional[Mapping[str, type]] = None):
549542
self.serialize_type = {
550543
"iso-8601": Serializer.serialize_iso,
551544
"rfc-1123": Serializer.serialize_rfc,
@@ -561,7 +554,7 @@ def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]] = None):
561554
"[]": self.serialize_iter,
562555
"{}": self.serialize_dict,
563556
}
564-
self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
557+
self.dependencies: Dict[str, type] = dict(classes) if classes else {}
565558
self.key_transformer = full_restapi_key_transformer
566559
self.client_side_validation = True
567560

@@ -649,7 +642,7 @@ def _serialize(self, target_obj, data_type=None, **kwargs):
649642
else: # That's a basic type
650643
# Integrate namespace if necessary
651644
local_node = _create_xml_node(xml_name, xml_prefix, xml_ns)
652-
local_node.text = unicode_str(new_attr)
645+
local_node.text = str(new_attr)
653646
serialized.append(local_node) # type: ignore
654647
else: # JSON
655648
for k in reversed(keys): # type: ignore
@@ -994,7 +987,7 @@ def serialize_object(self, attr, **kwargs):
994987
return self.serialize_basic(attr, self.basic_types[obj_type], **kwargs)
995988
if obj_type is _long_type:
996989
return self.serialize_long(attr)
997-
if obj_type is unicode_str:
990+
if obj_type is str:
998991
return self.serialize_unicode(attr)
999992
if obj_type is datetime.datetime:
1000993
return self.serialize_iso(attr)
@@ -1370,7 +1363,7 @@ class Deserializer(object):
13701363

13711364
valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?")
13721365

1373-
def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]] = None):
1366+
def __init__(self, classes: Optional[Mapping[str, type]] = None):
13741367
self.deserialize_type = {
13751368
"iso-8601": Deserializer.deserialize_iso,
13761369
"rfc-1123": Deserializer.deserialize_rfc,
@@ -1390,7 +1383,7 @@ def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]] = None):
13901383
"duration": (isodate.Duration, datetime.timedelta),
13911384
"iso-8601": (datetime.datetime),
13921385
}
1393-
self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
1386+
self.dependencies: Dict[str, type] = dict(classes) if classes else {}
13941387
self.key_extractors = [rest_key_extractor, xml_key_extractor]
13951388
# Additional properties only works if the "rest_key_extractor" is used to
13961389
# extract the keys. Making it to work whatever the key extractor is too much
@@ -1443,7 +1436,7 @@ def _deserialize(self, target_obj, data):
14431436

14441437
response, class_name = self._classify_target(target_obj, data)
14451438

1446-
if isinstance(response, basestring):
1439+
if isinstance(response, str):
14471440
return self.deserialize_data(data, response)
14481441
elif isinstance(response, type) and issubclass(response, Enum):
14491442
return self.deserialize_enum(data, response)
@@ -1514,14 +1507,14 @@ def _classify_target(self, target, data):
15141507
if target is None:
15151508
return None, None
15161509

1517-
if isinstance(target, basestring):
1510+
if isinstance(target, str):
15181511
try:
15191512
target = self.dependencies[target]
15201513
except KeyError:
15211514
return target, target
15221515

15231516
try:
1524-
target = target._classify(data, self.dependencies)
1517+
target = target._classify(data, self.dependencies) # type: ignore
15251518
except AttributeError:
15261519
pass # Target is not a Model, no classify
15271520
return target, target.__class__.__name__ # type: ignore
@@ -1577,7 +1570,7 @@ def _unpack_content(raw_data, content_type=None):
15771570
if hasattr(raw_data, "_content_consumed"):
15781571
return RawDeserializer.deserialize_from_http_generics(raw_data.text, raw_data.headers)
15791572

1580-
if isinstance(raw_data, (basestring, bytes)) or hasattr(raw_data, "read"):
1573+
if isinstance(raw_data, (str, bytes)) or hasattr(raw_data, "read"):
15811574
return RawDeserializer.deserialize_from_text(raw_data, content_type) # type: ignore
15821575
return raw_data
15831576

@@ -1699,7 +1692,7 @@ def deserialize_object(self, attr, **kwargs):
16991692
if isinstance(attr, ET.Element):
17001693
# Do no recurse on XML, just return the tree as-is
17011694
return attr
1702-
if isinstance(attr, basestring):
1695+
if isinstance(attr, str):
17031696
return self.deserialize_basic(attr, "str")
17041697
obj_type = type(attr)
17051698
if obj_type in self.basic_types:
@@ -1756,7 +1749,7 @@ def deserialize_basic(self, attr, data_type):
17561749
if data_type == "bool":
17571750
if attr in [True, False, 1, 0]:
17581751
return bool(attr)
1759-
elif isinstance(attr, basestring):
1752+
elif isinstance(attr, str):
17601753
if attr.lower() in ["true", "1"]:
17611754
return True
17621755
elif attr.lower() in ["false", "0"]:

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/_generated/aio/operations/_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,7 @@ async def mute(
12841284

12851285
response = pipeline_response.http_response
12861286

1287-
if response.status_code not in [202]:
1287+
if response.status_code not in [200]:
12881288
if _stream:
12891289
await response.read() # Load the body in memory and close the socket
12901290
map_error(status_code=response.status_code, response=response, error_map=error_map)

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/_generated/models/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from ._models import AddParticipantResponse
1212
from ._models import AddParticipantSucceeded
1313
from ._models import AnswerCallRequest
14+
from ._models import AnswerFailed
1415
from ._models import AzureOpenAIDialog
1516
from ._models import AzureOpenAIDialogUpdate
1617
from ._models import BaseDialog
@@ -39,6 +40,7 @@
3940
from ._models import ContinuousDtmfRecognitionStopped
4041
from ._models import ContinuousDtmfRecognitionToneFailed
4142
from ._models import ContinuousDtmfRecognitionToneReceived
43+
from ._models import CreateCallFailed
4244
from ._models import CreateCallRequest
4345
from ._models import CustomCallingContext
4446
from ._models import DialogCompleted
@@ -146,6 +148,7 @@
146148
"AddParticipantResponse",
147149
"AddParticipantSucceeded",
148150
"AnswerCallRequest",
151+
"AnswerFailed",
149152
"AzureOpenAIDialog",
150153
"AzureOpenAIDialogUpdate",
151154
"BaseDialog",
@@ -174,6 +177,7 @@
174177
"ContinuousDtmfRecognitionStopped",
175178
"ContinuousDtmfRecognitionToneFailed",
176179
"ContinuousDtmfRecognitionToneReceived",
180+
"CreateCallFailed",
177181
"CreateCallRequest",
178182
"CustomCallingContext",
179183
"DialogCompleted",

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/_generated/models/_models.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,51 @@ def __init__(
359359
self.source_caller_id_number = source_caller_id_number
360360

361361

362+
class AnswerFailed(_serialization.Model):
363+
"""AnswerFailed event.
364+
365+
Variables are only populated by the server, and will be ignored when sending a request.
366+
367+
:ivar operation_context: Used by customers when calling mid-call actions to correlate the
368+
request to the response event.
369+
:vartype operation_context: str
370+
:ivar result_information: Contains the resulting SIP code, sub-code and message.
371+
:vartype result_information: ~azure.communication.callautomation.models.ResultInformation
372+
:ivar call_connection_id: Call connection ID.
373+
:vartype call_connection_id: str
374+
:ivar server_call_id: Server call ID.
375+
:vartype server_call_id: str
376+
:ivar correlation_id: Correlation ID for event to call correlation. Also called ChainId for
377+
skype chain ID.
378+
:vartype correlation_id: str
379+
"""
380+
381+
_validation = {
382+
"operation_context": {"readonly": True},
383+
"result_information": {"readonly": True},
384+
"call_connection_id": {"readonly": True},
385+
"server_call_id": {"readonly": True},
386+
"correlation_id": {"readonly": True},
387+
}
388+
389+
_attribute_map = {
390+
"operation_context": {"key": "operationContext", "type": "str"},
391+
"result_information": {"key": "resultInformation", "type": "ResultInformation"},
392+
"call_connection_id": {"key": "callConnectionId", "type": "str"},
393+
"server_call_id": {"key": "serverCallId", "type": "str"},
394+
"correlation_id": {"key": "correlationId", "type": "str"},
395+
}
396+
397+
def __init__(self, **kwargs: Any) -> None:
398+
""" """
399+
super().__init__(**kwargs)
400+
self.operation_context = None
401+
self.result_information = None
402+
self.call_connection_id = None
403+
self.server_call_id = None
404+
self.correlation_id = None
405+
406+
362407
class BaseDialog(_serialization.Model):
363408
"""BaseDialog.
364409
@@ -612,7 +657,7 @@ class CallConnectionProperties(_serialization.Model): # pylint: disable=too-man
612657
"source": {"key": "source", "type": "CommunicationIdentifierModel"},
613658
"correlation_id": {"key": "correlationId", "type": "str"},
614659
"answered_by": {"key": "answeredBy", "type": "CommunicationUserIdentifierModel"},
615-
"original_pstn_target": {"key": "originalPSTNTarget", "type": "PhoneNumberIdentifierModel"},
660+
"original_pstn_target": {"key": "originalPstnTarget", "type": "PhoneNumberIdentifierModel"},
616661
}
617662

618663
def __init__(
@@ -1610,6 +1655,51 @@ def __init__(self, *, tone: Optional[Union[str, "_models.DtmfTone"]] = None, **k
16101655
self.correlation_id = None
16111656

16121657

1658+
class CreateCallFailed(_serialization.Model):
1659+
"""The CreateCallFailed event.
1660+
1661+
Variables are only populated by the server, and will be ignored when sending a request.
1662+
1663+
:ivar operation_context: Used by customers when calling mid-call actions to correlate the
1664+
request to the response event.
1665+
:vartype operation_context: str
1666+
:ivar result_information: Contains the resulting SIP code, sub-code and message.
1667+
:vartype result_information: ~azure.communication.callautomation.models.ResultInformation
1668+
:ivar call_connection_id: Call connection ID.
1669+
:vartype call_connection_id: str
1670+
:ivar server_call_id: Server call ID.
1671+
:vartype server_call_id: str
1672+
:ivar correlation_id: Correlation ID for event to call correlation. Also called ChainId for
1673+
skype chain ID.
1674+
:vartype correlation_id: str
1675+
"""
1676+
1677+
_validation = {
1678+
"operation_context": {"readonly": True},
1679+
"result_information": {"readonly": True},
1680+
"call_connection_id": {"readonly": True},
1681+
"server_call_id": {"readonly": True},
1682+
"correlation_id": {"readonly": True},
1683+
}
1684+
1685+
_attribute_map = {
1686+
"operation_context": {"key": "operationContext", "type": "str"},
1687+
"result_information": {"key": "resultInformation", "type": "ResultInformation"},
1688+
"call_connection_id": {"key": "callConnectionId", "type": "str"},
1689+
"server_call_id": {"key": "serverCallId", "type": "str"},
1690+
"correlation_id": {"key": "correlationId", "type": "str"},
1691+
}
1692+
1693+
def __init__(self, **kwargs: Any) -> None:
1694+
""" """
1695+
super().__init__(**kwargs)
1696+
self.operation_context = None
1697+
self.result_information = None
1698+
self.call_connection_id = None
1699+
self.server_call_id = None
1700+
self.correlation_id = None
1701+
1702+
16131703
class CreateCallRequest(_serialization.Model):
16141704
"""The request payload for creating the call.
16151705

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/_generated/operations/_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2245,7 +2245,7 @@ def mute(
22452245

22462246
response = pipeline_response.http_response
22472247

2248-
if response.status_code not in [202]:
2248+
if response.status_code not in [200]:
22492249
if _stream:
22502250
response.read() # Load the body in memory and close the socket
22512251
map_error(status_code=response.status_code, response=response, error_map=error_map)

sdk/communication/azure-communication-callautomation/azure/communication/callautomation/aio/_call_automation_client_async.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ async def answer_call(
311311
operation_context: Optional[str] = None,
312312
media_streaming_configuration: Optional['MediaStreamingConfiguration'] = None,
313313
transcription_configuration: Optional['TranscriptionConfiguration'] = None,
314+
source_caller_id_number: Optional['PhoneNumberIdentifier'] = None,
314315
**kwargs
315316
) -> CallConnectionProperties:
316317
"""Answer incoming call with Azure Communication Service's IncomingCall event
@@ -333,6 +334,9 @@ async def answer_call(
333334
or None
334335
:keyword media_streaming_configuration: Media Streaming Configuration.
335336
:paramtype media_streaming_configuration: ~azure.communication.callautomation.MediaStreamingConfiguration
337+
:source_caller_id_number: The source caller Id, a phone number, that's will be used when
338+
inviting a pstn target. Required only when transferring call to PSTN, if this is an incoming voip call.
339+
:vartype source_caller_id_number: ~azure.communication.callautomation.PhoneNumberIdentifier
336340
:return: CallConnectionProperties
337341
:rtype: ~azure.communication.callautomation.CallConnectionProperties
338342
:raises ~azure.core.exceptions.HttpResponseError:
@@ -344,6 +348,7 @@ async def answer_call(
344348
answer_call_request = AnswerCallRequest(
345349
incoming_call_context=incoming_call_context,
346350
callback_uri=callback_url,
351+
source_caller_id_number=serialize_phone_identifier(source_caller_id_number),
347352
media_streaming_configuration=media_streaming_configuration.to_generated(
348353
) if media_streaming_configuration else None,
349354
transcription_configuration=transcription_configuration.to_generated()

sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ autorest SWAGGER.md
1717

1818
```yaml
1919
tag: package-2023-10-03-preview
20-
require: https://github.com/Azure/azure-rest-api-specs/blob/384aedb56cfbadfa16ccb35737eab58dfeae81c5/specification/communication/data-plane/CallAutomation/readme.md
20+
require: https://github.com/Azure/azure-rest-api-specs/blob/3abbcd8fe3f0527560c7db59255f654366e965d8/specification/communication/data-plane/CallAutomation/readme.md
2121
output-folder: ../azure/communication/callautomation/_generated
2222
models-mode: msrest
2323
namespace: azure.communication.callautomation

sdk/communication/azure-communication-callautomation/tests/test_call_connection_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def mock_send(_, **kwargs):
217217
kwargs.pop("stream", None)
218218
if kwargs:
219219
raise ValueError(f"Received unexpected kwargs in transport: {kwargs}")
220-
return mock_response(status_code=202, json_payload={
220+
return mock_response(status_code=200, json_payload={
221221
"operationContext": self.operation_context})
222222

223223
call_connection = CallConnectionClient(

sdk/communication/azure-communication-callautomation/tests/test_e2e_media_client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
from datetime import timedelta
7+
import pytest
78
import time
89
import unittest
910

@@ -86,7 +87,8 @@ def test_dtmf_actions_in_a_call(self):
8687
self.terminate_call(unique_id)
8788
return
8889

89-
@recorded_by_proxy
90+
@pytest.mark.skip(reason="disabling this test due to status code change in the muteparticipant service, current test data doesnt have the change. Created work item to update the test data https://skype.visualstudio.com/SPOOL/_workitems/edit/3602301")
91+
@recorded_by_proxy
9092
def test_add_and_mute_participant_in_a_call(self):
9193

9294
# try to establish the call

0 commit comments

Comments
 (0)