Skip to content

Commit dda7a48

Browse files
authored
SIP X-* Headers support- Incoming and Transfer (Azure#49061)
* Added Sip X-* header support for Incoming call and transfer call through CustomCallingContext. * Fixed autorest. * Verifying sip header changes with language specific requirements.
1 parent 73b8807 commit dda7a48

File tree

6 files changed

+154
-8
lines changed

6 files changed

+154
-8
lines changed

sdk/communication/Azure.Communication.CallAutomation/api/Azure.Communication.CallAutomation.net8.0.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -819,8 +819,13 @@ internal CustomCallingContext() { }
819819
public System.Collections.Generic.IDictionary<string, string> SipHeaders { get { throw null; } }
820820
public System.Collections.Generic.IDictionary<string, string> VoipHeaders { get { throw null; } }
821821
public void AddSipUui(string value) { }
822-
public void AddSipX(string key, string value) { }
822+
public void AddSipX(string key, string value, Azure.Communication.CallAutomation.CustomCallingContext.SipHeaderPrefix prefix = Azure.Communication.CallAutomation.CustomCallingContext.SipHeaderPrefix.XMSCustom) { }
823823
public void AddVoip(string key, string value) { }
824+
public enum SipHeaderPrefix
825+
{
826+
XMSCustom = 0,
827+
X = 1,
828+
}
824829
}
825830
public partial class DialogCompleted : Azure.Communication.CallAutomation.CallAutomationEventBase
826831
{

sdk/communication/Azure.Communication.CallAutomation/api/Azure.Communication.CallAutomation.netstandard2.0.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,8 +818,13 @@ internal CustomCallingContext() { }
818818
public System.Collections.Generic.IDictionary<string, string> SipHeaders { get { throw null; } }
819819
public System.Collections.Generic.IDictionary<string, string> VoipHeaders { get { throw null; } }
820820
public void AddSipUui(string value) { }
821-
public void AddSipX(string key, string value) { }
821+
public void AddSipX(string key, string value, Azure.Communication.CallAutomation.CustomCallingContext.SipHeaderPrefix prefix = Azure.Communication.CallAutomation.CustomCallingContext.SipHeaderPrefix.XMSCustom) { }
822822
public void AddVoip(string key, string value) { }
823+
public enum SipHeaderPrefix
824+
{
825+
XMSCustom = 0,
826+
X = 1,
827+
}
823828
}
824829
public partial class DialogCompleted : Azure.Communication.CallAutomation.CallAutomationEventBase
825830
{

sdk/communication/Azure.Communication.CallAutomation/src/Models/CallInvite.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class CallInvite
1313
/// <summary>
1414
/// Creates a new CallInvite object.
1515
/// When the source of the call is a Teams App source, callerIdNumber is not supported and should be null.
16+
/// Sip Headers are supported for PSTN calls only. Voip Headers are not supported.
1617
/// </summary>
1718
/// <param name="targetPhoneNumberIdentity"></param>
1819
/// <param name="callerIdNumber"></param>
@@ -25,6 +26,7 @@ public CallInvite(PhoneNumberIdentifier targetPhoneNumberIdentity, PhoneNumberId
2526

2627
/// <summary>
2728
/// Creates a new CallInvite object.
29+
/// Sip Headers are not supported. Voip Headers are supported for ACS Users.
2830
/// </summary>
2931
/// <param name="targetIdentity"></param>
3032
public CallInvite(CommunicationUserIdentifier targetIdentity)
@@ -35,6 +37,7 @@ public CallInvite(CommunicationUserIdentifier targetIdentity)
3537

3638
/// <summary>
3739
/// Creates a new CallInvite object.
40+
/// Sip Headers are not supported. Voip Headers are supported for Microsoft teams Users.
3841
/// </summary>
3942
/// <param name="targetIdentity"></param>
4043
public CallInvite(MicrosoftTeamsUserIdentifier targetIdentity)
@@ -45,6 +48,7 @@ public CallInvite(MicrosoftTeamsUserIdentifier targetIdentity)
4548

4649
/// <summary>
4750
/// Creates a new CallInvite object.
51+
/// Sip Headers are not supported. Voip Headers are supported for Microsoft Teams Apps.
4852
/// </summary>
4953
/// <param name="targetIdentity"></param>
5054
public CallInvite(MicrosoftTeamsAppIdentifier targetIdentity)

sdk/communication/Azure.Communication.CallAutomation/src/Models/CustomCallingContext.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,21 @@ public void AddSipUui(string value)
4444
/// </summary>
4545
/// <param name="key">custom calling context sip X header's key.</param>
4646
/// <param name="value">custom calling context sip X header's value.</param>
47-
public void AddSipX(string key, string value)
47+
/// <param name="prefix">prefix to be used for SIP X headers.</param>
48+
public void AddSipX(string key, string value, SipHeaderPrefix prefix = SipHeaderPrefix.XMSCustom)
4849
{
4950
if (SipHeaders == null)
5051
{
5152
throw new InvalidOperationException("Cannot add sip X header, SipHeaders is null.");
5253
}
53-
SipHeaders.Add("X-MS-Custom-" + key, value);
54+
if (prefix == SipHeaderPrefix.XMSCustom)
55+
{
56+
SipHeaders.Add("X-MS-Custom-" + key, value);
57+
}
58+
else
59+
{
60+
SipHeaders.Add("X-" + key, value);
61+
}
5462
}
5563

5664
/// <summary>
@@ -66,5 +74,21 @@ public void AddVoip(string key, string value)
6674
}
6775
VoipHeaders.Add(key, value);
6876
}
77+
78+
/// <summary>
79+
/// Enum representing the prefix to be used for SIP X headers.
80+
/// </summary>
81+
public enum SipHeaderPrefix
82+
{
83+
/// <summary>
84+
/// Use the legacy "X-MS-Custom-" prefix.
85+
/// </summary>
86+
XMSCustom = 0,
87+
88+
/// <summary>
89+
/// Use the generic "X-" prefix.
90+
/// </summary>
91+
X = 1
92+
}
6993
}
7094
}

sdk/communication/Azure.Communication.CallAutomation/tests/CallConnections/CallConnectionTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,23 @@ public async Task TransferCallToParticipantAsync_simpleMethod_MicrosoftTeamsAppA
142142
verifyOperationContext(response);
143143
}
144144

145+
[TestCaseSource(nameof(TestData_TransferCallToParticipant_PhoneNumberIdentifier))]
146+
public async Task TransferCallToParticipantAsync_simpleMethod_PhoneNumberIdentifier_202Accepted(CallInvite callInvite)
147+
{
148+
var callConnection = CreateMockCallConnection(202, OperationContextPayload);
149+
var response = await callConnection.TransferCallToParticipantAsync(callInvite.Target).ConfigureAwait(false);
150+
Assert.AreEqual((int)HttpStatusCode.Accepted, response.GetRawResponse().Status);
151+
verifyOperationContext(response);
152+
}
153+
154+
[TestCaseSource(nameof(TestData_TransferCallToParticipant_PhoneNumberIdentifier_MS))]
155+
public async Task TransferCallToParticipantAsync_simpleMethod_PhoneNumberIdentifier_MSAsTarget_202Accepted(CallInvite callInvite)
156+
{
157+
var callConnection = CreateMockCallConnection(202, OperationContextPayload);
158+
var response = await callConnection.TransferCallToParticipantAsync(callInvite.Target).ConfigureAwait(false);
159+
Assert.AreEqual((int)HttpStatusCode.Accepted, response.GetRawResponse().Status);
160+
verifyOperationContext(response);
161+
}
145162
[TestCaseSource(nameof(TestData_TransferCallToParticipant))]
146163
public async Task TransferCallToParticipantAsync_202Accepted(CallInvite callInvite)
147164
{
@@ -578,6 +595,31 @@ private CallConnection CreateMockCallConnection(int responseCode, string? respon
578595
};
579596
}
580597

598+
private static IEnumerable<object?[]> TestData_TransferCallToParticipant_PhoneNumberIdentifier_MS()
599+
{
600+
var callInvite = new CallInvite(new PhoneNumberIdentifier(PhoneNumber), new PhoneNumberIdentifier("+17654321"));
601+
callInvite.CustomCallingContext.AddSipX("key1", "value1", CustomCallingContext.SipHeaderPrefix.XMSCustom);
602+
return new[]
603+
{
604+
new object?[]
605+
{
606+
callInvite
607+
},
608+
};
609+
}
610+
private static IEnumerable<object?[]> TestData_TransferCallToParticipant_PhoneNumberIdentifier()
611+
{
612+
var callInvite = new CallInvite(new PhoneNumberIdentifier(PhoneNumber), new PhoneNumberIdentifier("+17654321"));
613+
callInvite.CustomCallingContext.AddSipX("key1", "value1", CustomCallingContext.SipHeaderPrefix.X);
614+
return new[]
615+
{
616+
new object?[]
617+
{
618+
callInvite
619+
},
620+
};
621+
}
622+
581623
private static IEnumerable<object?[]> TestData_GetParticipant()
582624
{
583625
return new[]

sdk/communication/Azure.Communication.CallAutomation/tests/Events/CallAutomationEventParserTests.cs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// Licensed under the MIT License.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Linq;
76
using System.Reflection;
7+
using System.Collections.Generic;
88
using System.Text.Json;
99
using Azure.Messaging;
1010
using Newtonsoft.Json.Linq;
@@ -1403,7 +1403,7 @@ public void MediaStreamingFailedEventParsed_Test()
14031403
}
14041404

14051405
[Test]
1406-
public void IncomingCallEventParsed_Test()
1406+
public void IncomingCallEventParsed_XMS_Header_Test()
14071407
{
14081408
// arrange
14091409
var to = new CommunicationUserIdentifier("8:acs:12345");
@@ -1413,8 +1413,8 @@ public void IncomingCallEventParsed_Test()
14131413

14141414
Dictionary<string, string> sipHeaders = new Dictionary<string, string>();
14151415
Dictionary<string, string> voipHeaders = new Dictionary<string, string>();
1416-
var customContext = new CustomCallingContext(voipHeaders, sipHeaders);
1417-
customContext.AddSipX("Test-SIP-Header", "TestSIPValue");
1416+
var customContext = new CustomCallingContext(sipHeaders, voipHeaders);
1417+
customContext.AddSipX("Test-SIP-Header", "TestSIPValue", CustomCallingContext.SipHeaderPrefix.XMSCustom);
14181418
customContext.AddVoip("Test-VoIP-Header", "TestVoIPValue");
14191419

14201420
var incomingCallContext = "incomingCallContext";
@@ -1445,6 +1445,72 @@ public void IncomingCallEventParsed_Test()
14451445
Assert.AreEqual(incomingCallContext, IncomingCall.IncomingCallContext);
14461446
Assert.AreEqual(onBehalfOfCallee.RawId, IncomingCall.OnBehalfOfCallee.RawId);
14471447
Assert.AreEqual(correlationId, IncomingCall.CorrelationId);
1448+
1449+
var sipHeaderKey = IncomingCall.CustomContext.SipHeaders.First().Key;
1450+
var sipHeaderValue = IncomingCall.CustomContext.SipHeaders.First().Value;
1451+
Console.WriteLine($"SIP Header -> Key: {sipHeaderKey}, Value: {sipHeaderValue}");
1452+
1453+
var voipHeaderKey = IncomingCall.CustomContext.VoipHeaders.First().Key;
1454+
var voipHeaderValue = IncomingCall.CustomContext.VoipHeaders.First().Value;
1455+
Console.WriteLine($"VoIP Header -> Key: {voipHeaderKey}, Value: {voipHeaderValue}");
1456+
}
1457+
else
1458+
{
1459+
Assert.Fail("Event parsed wrongfully");
1460+
}
1461+
}
1462+
1463+
[Test]
1464+
public void IncomingCallEventParsed_X_Header_Test()
1465+
{
1466+
// arrange
1467+
var to = new CommunicationUserIdentifier("8:acs:12345");
1468+
var from = new CommunicationUserIdentifier("8:acs:54321");
1469+
var callerDisplayName = "callerDisplayName";
1470+
var serverCallId = "serverCallId";
1471+
1472+
Dictionary<string, string> sipHeaders = new Dictionary<string, string>();
1473+
Dictionary<string, string> voipHeaders = new Dictionary<string, string>();
1474+
var customContext = new CustomCallingContext(sipHeaders, voipHeaders);
1475+
customContext.AddSipX("Test-SIP-Header", "TestSIPValue", CustomCallingContext.SipHeaderPrefix.X);
1476+
customContext.AddVoip("Test-VoIP-Header", "TestVoIPValue");
1477+
1478+
var incomingCallContext = "incomingCallContext";
1479+
var onBehalfOfCallee = from;
1480+
var correlationId = "correlationId";
1481+
var @event = CallAutomationModelFactory.IncomingCall(to, from, callerDisplayName, serverCallId, customContext, incomingCallContext, onBehalfOfCallee, correlationId);
1482+
1483+
JsonSerializerOptions jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
1484+
string jsonEvent = JsonSerializer.Serialize(@event, jsonOptions);
1485+
1486+
// act
1487+
var parsedEvent = CallAutomationEventParser.Parse(jsonEvent, "Microsoft.Communication.IncomingCall");
1488+
var IncomingCall = (IncomingCall)parsedEvent;
1489+
1490+
// assert
1491+
if (parsedEvent is IncomingCall)
1492+
{
1493+
Assert.AreEqual(to.RawId, IncomingCall.To.RawId);
1494+
Assert.AreEqual(from.RawId, IncomingCall.From.RawId);
1495+
Assert.AreEqual(callerDisplayName, IncomingCall.CallerDisplayName);
1496+
Assert.AreEqual(serverCallId, IncomingCall.ServerCallId);
1497+
1498+
Assert.AreEqual(customContext.SipHeaders.First().Key, IncomingCall.CustomContext.SipHeaders.First().Key);
1499+
Assert.AreEqual(customContext.SipHeaders.First().Value, IncomingCall.CustomContext.SipHeaders.First().Value);
1500+
Assert.AreEqual(customContext.VoipHeaders.First().Key, IncomingCall.CustomContext.VoipHeaders.First().Key);
1501+
Assert.AreEqual(customContext.VoipHeaders.First().Value, IncomingCall.CustomContext.VoipHeaders.First().Value);
1502+
1503+
Assert.AreEqual(incomingCallContext, IncomingCall.IncomingCallContext);
1504+
Assert.AreEqual(onBehalfOfCallee.RawId, IncomingCall.OnBehalfOfCallee.RawId);
1505+
Assert.AreEqual(correlationId, IncomingCall.CorrelationId);
1506+
1507+
var sipHeaderKey = IncomingCall.CustomContext.SipHeaders.First().Key;
1508+
var sipHeaderValue = IncomingCall.CustomContext.SipHeaders.First().Value;
1509+
Console.WriteLine($"SIP Header -> Key: {sipHeaderKey}, Value: {sipHeaderValue}");
1510+
1511+
var voipHeaderKey = IncomingCall.CustomContext.VoipHeaders.First().Key;
1512+
var voipHeaderValue = IncomingCall.CustomContext.VoipHeaders.First().Value;
1513+
Console.WriteLine($"VoIP Header -> Key: {voipHeaderKey}, Value: {voipHeaderValue}");
14481514
}
14491515
else
14501516
{

0 commit comments

Comments
 (0)