diff --git a/sdk/communication/Azure.Communication.CallAutomation/api/Azure.Communication.CallAutomation.netstandard2.0.cs b/sdk/communication/Azure.Communication.CallAutomation/api/Azure.Communication.CallAutomation.netstandard2.0.cs index d2f53d639d6b..29cb16e2cc56 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/api/Azure.Communication.CallAutomation.netstandard2.0.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/api/Azure.Communication.CallAutomation.netstandard2.0.cs @@ -112,6 +112,7 @@ public CallAutomationClient(string connectionString, Azure.Communication.CallAut public CallAutomationClient(System.Uri endpoint, Azure.Core.TokenCredential credential, Azure.Communication.CallAutomation.CallAutomationClientOptions options = null) { } public CallAutomationClient(System.Uri pmaEndpoint, string connectionString, Azure.Communication.CallAutomation.CallAutomationClientOptions options = null) { } public CallAutomationClient(System.Uri pmaEndpoint, System.Uri acsEndpoint, Azure.Core.TokenCredential credential, Azure.Communication.CallAutomation.CallAutomationClientOptions options = null) { } + public Azure.Communication.MicrosoftTeamsAppIdentifier OPSSource { get { throw null; } } public Azure.Communication.CommunicationUserIdentifier Source { get { throw null; } } public virtual Azure.Response AnswerCall(Azure.Communication.CallAutomation.AnswerCallOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response AnswerCall(string incomingCallContext, System.Uri callbackUri, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -138,6 +139,7 @@ public CallAutomationClient(System.Uri pmaEndpoint, System.Uri acsEndpoint, Azur public partial class CallAutomationClientOptions : Azure.Core.ClientOptions { public CallAutomationClientOptions(Azure.Communication.CallAutomation.CallAutomationClientOptions.ServiceVersion version = Azure.Communication.CallAutomation.CallAutomationClientOptions.ServiceVersion.V2023_10_03_Preview) { } + public Azure.Communication.MicrosoftTeamsAppIdentifier OPSSource { get { throw null; } set { } } public Azure.Communication.CommunicationUserIdentifier Source { get { throw null; } set { } } public enum ServiceVersion { @@ -341,6 +343,7 @@ public CallIntelligenceOptions() { } public partial class CallInvite { public CallInvite(Azure.Communication.CommunicationUserIdentifier targetIdentity) { } + public CallInvite(Azure.Communication.MicrosoftTeamsAppIdentifier targetIdentity) { } public CallInvite(Azure.Communication.MicrosoftTeamsUserIdentifier targetIdentity) { } public CallInvite(Azure.Communication.PhoneNumberIdentifier targetPhoneNumberIdentity, Azure.Communication.PhoneNumberIdentifier callerIdNumber) { } public Azure.Communication.CallAutomation.CustomCallingContext CustomCallingContext { get { throw null; } } diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClient.cs b/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClient.cs index 87af4ccc0892..a63950ff4b85 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClient.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClient.cs @@ -36,6 +36,14 @@ public class CallAutomationClient /// public CommunicationUserIdentifier Source { get; } + /// + /// MicrosoftTeamsAppIdentifier that makes the outbound call. + /// This can be provided by providing CallAutomationClientOption during construction of CallAutomationClient. + /// If left blank, Source is the default outbound call identity. + /// This should be mutual exclusive with Source. + /// + public MicrosoftTeamsAppIdentifier OPSSource { get; } + #region public constructors /// Initializes a new instance of . /// Connection string acquired from the Azure Communication Services resource. @@ -119,6 +127,7 @@ private CallAutomationClient(Uri endpoint, HttpPipeline httpPipeline, CallAutoma CallDialogRestClient = new CallDialogRestClient(_clientDiagnostics, httpPipeline, endpoint, options.ApiVersion); EventProcessor = new CallAutomationEventProcessor(); Source = options.Source; + OPSSource = options.OPSSource; } private CallAutomationClient( @@ -620,6 +629,7 @@ private CreateCallRequestInternal CreateCallRequest(CreateCallOptions options) : new PhoneNumberIdentifierModel(options?.CallInvite?.SourceCallerIdNumber?.PhoneNumber), SourceDisplayName = options?.CallInvite?.SourceDisplayName, Source = Source == null ? null : new CommunicationUserIdentifierModel(Source.Id), + OpsSource = OPSSource == null ? null : new MicrosoftTeamsAppIdentifierModel(OPSSource.AppId), }; request.CustomCallingContext = new CustomCallingContextInternal( @@ -654,6 +664,7 @@ private CreateCallRequestInternal CreateCallRequest(CreateGroupCallOptions optio : new PhoneNumberIdentifierModel(options?.SourceCallerIdNumber?.PhoneNumber), SourceDisplayName = options?.SourceDisplayName, Source = Source == null ? null : new CommunicationUserIdentifierModel(Source.Id), + OpsSource = OPSSource == null ? null : new MicrosoftTeamsAppIdentifierModel(OPSSource.AppId) }; request.CustomCallingContext = new CustomCallingContextInternal( diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClientOptions.cs b/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClientOptions.cs index 488f1ce38121..6b04dd4a45a3 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClientOptions.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/src/CallAutomationClientOptions.cs @@ -22,9 +22,16 @@ public class CallAutomationClientOptions : ClientOptions /// /// The caller source of the call automation client. + /// Mutual exclusive with . /// public CommunicationUserIdentifier Source { get; set; } + /// + /// The One Phone System caller source of the call automation client. + /// Mutual exclusive with . + /// + public MicrosoftTeamsAppIdentifier OPSSource { get; set; } + /// /// Initializes a new instance of the . /// diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/Generated/CallDialogRestClient.cs b/sdk/communication/Azure.Communication.CallAutomation/src/Generated/CallDialogRestClient.cs index a82b7a6ce2e0..34dc32e28db9 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/Generated/CallDialogRestClient.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/src/Generated/CallDialogRestClient.cs @@ -155,10 +155,9 @@ internal HttpMessage CreateStopDialogRequest(string callConnectionId, string dia return message; } - /// Stop a dialog. - /// The call connection id. - /// The dialog id. - /// Operation callback URI. + /// The to use. + /// The to use. + /// The to use. /// The cancellation token to use. /// or is null. public async Task StopDialogAsync(string callConnectionId, string dialogId, string operationCallbackUri = null, CancellationToken cancellationToken = default) @@ -183,10 +182,9 @@ public async Task StopDialogAsync(string callConnectionId, string dial } } - /// Stop a dialog. - /// The call connection id. - /// The dialog id. - /// Operation callback URI. + /// The to use. + /// The to use. + /// The to use. /// The cancellation token to use. /// or is null. public Response StopDialog(string callConnectionId, string dialogId, string operationCallbackUri = null, CancellationToken cancellationToken = default) diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.Serialization.cs b/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.Serialization.cs index a36c1ba323cb..797fb28b64d4 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.Serialization.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.Serialization.cs @@ -37,6 +37,11 @@ void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) writer.WritePropertyName("source"u8); writer.WriteObjectValue(Source); } + if (Optional.IsDefined(OpsSource)) + { + writer.WritePropertyName("opsSource"u8); + writer.WriteObjectValue(OpsSource); + } if (Optional.IsDefined(OperationContext)) { writer.WritePropertyName("operationContext"u8); diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.cs b/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.cs index cd0c27a9b87a..433e67d20bf5 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/src/Generated/Models/CreateCallRequestInternal.cs @@ -35,18 +35,20 @@ public CreateCallRequestInternal(IEnumerable targe /// /// Display name of the call if dialing out to a pstn number. /// The identifier of the source of the call. + /// The identifier of the source in an OPS call. /// A customer set value used to track the answering of a call. /// The callback URI. /// Media Streaming Configuration. /// Live Transcription Configuration. /// AI options for the call. /// Used by customer to send custom calling context to targets. - internal CreateCallRequestInternal(IList targets, PhoneNumberIdentifierModel sourceCallerIdNumber, string sourceDisplayName, CommunicationUserIdentifierModel source, string operationContext, string callbackUri, MediaStreamingOptionsInternal mediaStreamingConfiguration, TranscriptionOptionsInternal transcriptionConfiguration, CallIntelligenceOptionsInternal callIntelligenceOptions, CustomCallingContextInternal customCallingContext) + internal CreateCallRequestInternal(IList targets, PhoneNumberIdentifierModel sourceCallerIdNumber, string sourceDisplayName, CommunicationUserIdentifierModel source, MicrosoftTeamsAppIdentifierModel opsSource, string operationContext, string callbackUri, MediaStreamingOptionsInternal mediaStreamingConfiguration, TranscriptionOptionsInternal transcriptionConfiguration, CallIntelligenceOptionsInternal callIntelligenceOptions, CustomCallingContextInternal customCallingContext) { Targets = targets; SourceCallerIdNumber = sourceCallerIdNumber; SourceDisplayName = sourceDisplayName; Source = source; + OpsSource = opsSource; OperationContext = operationContext; CallbackUri = callbackUri; MediaStreamingConfiguration = mediaStreamingConfiguration; @@ -66,6 +68,8 @@ internal CreateCallRequestInternal(IList targets, public string SourceDisplayName { get; set; } /// The identifier of the source of the call. public CommunicationUserIdentifierModel Source { get; set; } + /// The identifier of the source in an OPS call. + public MicrosoftTeamsAppIdentifierModel OpsSource { get; set; } /// A customer set value used to track the answering of a call. public string OperationContext { get; set; } /// The callback URI. diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/Models/CallInvite.cs b/sdk/communication/Azure.Communication.CallAutomation/src/Models/CallInvite.cs index d0d27e043d23..0546eb7207a5 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/Models/CallInvite.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/src/Models/CallInvite.cs @@ -42,6 +42,16 @@ public CallInvite(MicrosoftTeamsUserIdentifier targetIdentity) CustomCallingContext = new CustomCallingContext(sipHeaders: null, voipHeaders: new Dictionary()); } + /// + /// Creates a new CallInvite object. + /// + /// + public CallInvite(MicrosoftTeamsAppIdentifier targetIdentity) + { + Target = targetIdentity; + CustomCallingContext = new CustomCallingContext(sipHeaders: null, voipHeaders: new Dictionary()); + } + /// /// The target callee. /// diff --git a/sdk/communication/Azure.Communication.CallAutomation/src/autorest.md b/sdk/communication/Azure.Communication.CallAutomation/src/autorest.md index 8b0babc7d314..1375dcb6ca1d 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/src/autorest.md +++ b/sdk/communication/Azure.Communication.CallAutomation/src/autorest.md @@ -10,7 +10,7 @@ model-namespace: false tag: package-2023-10-03-preview require: - - https://github.com/Azure/azure-rest-api-specs/blob/73e737262249936932d2f7a3e3c400ce2b14a92c/specification/communication/data-plane/CallAutomation/readme.md + - https://github.com/Azure/azure-rest-api-specs/blob/be2a0fa68829fcb15c4e6b47aa6bc4bdd566c1cf/specification/communication/data-plane/CallAutomation/readme.md title: Azure Communication Services diff --git a/sdk/communication/Azure.Communication.CallAutomation/tests/CallAutomationClients/CallAutomationClientTests.cs b/sdk/communication/Azure.Communication.CallAutomation/tests/CallAutomationClients/CallAutomationClientTests.cs index 609fc7ab3903..01d53df712a8 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/tests/CallAutomationClients/CallAutomationClientTests.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/tests/CallAutomationClients/CallAutomationClientTests.cs @@ -235,6 +235,38 @@ public void CreateCall_201Created(CallInvite target, Uri callbackUri) Assert.AreEqual(CallConnectionId, result.CallConnection.CallConnectionId); } + [TestCaseSource(nameof(TestData_CreateCall))] + public async Task CreateCallWithOPSSourceAsync_201Created(CallInvite target, Uri callbackUri) + { + CallAutomationClient callAutomationClient = CreateMockCallAutomationClient(201, CreateOrAnswerCallOrGetCallConnectionPayloadForOPSCall, isOPSCall:true); + + var options = new CreateCallOptions(target, callbackUri); + var response = await callAutomationClient.CreateCallAsync(options).ConfigureAwait(false); + CreateCallResult result = (CreateCallResult)response; + Assert.NotNull(result); + Assert.AreEqual((int)HttpStatusCode.Created, response.GetRawResponse().Status); + verifyOPSCallConnectionProperties(result.CallConnectionProperties); + Assert.Null(result.CallConnectionProperties.MediaSubscriptionId); + Assert.Null(result.CallConnectionProperties.DataSubscriptionId); + Assert.AreEqual(CallConnectionId, result.CallConnection.CallConnectionId); + } + + [TestCaseSource(nameof(TestData_CreateCall))] + public void CreateCallWithOPSSource_201Created(CallInvite target, Uri callbackUri) + { + CallAutomationClient callAutomationClient = CreateMockCallAutomationClient(201, CreateOrAnswerCallOrGetCallConnectionPayloadForOPSCall, isOPSCall: true); + + var options = new CreateCallOptions(target, callbackUri); + var response = callAutomationClient.CreateCall(options); + CreateCallResult result = (CreateCallResult)response; + Assert.NotNull(result); + Assert.AreEqual((int)HttpStatusCode.Created, response.GetRawResponse().Status); + verifyOPSCallConnectionProperties(result.CallConnectionProperties); + Assert.Null(result.CallConnectionProperties.MediaSubscriptionId); + Assert.Null(result.CallConnectionProperties.DataSubscriptionId); + Assert.AreEqual(CallConnectionId, result.CallConnection.CallConnectionId); + } + [TestCaseSource(nameof(TestData_CreateCall))] public async Task CreateCallWithOptionsAsync_201Created(CallInvite target, Uri callbackUri) { @@ -415,7 +447,19 @@ private static void ValidateCallConnectionProperties(CallConnectionProperties pr { new object?[] { - new CallInvite(new CommunicationUserIdentifier("12345")), + new CallInvite(new CommunicationUserIdentifier("8:acs:12345")), + new Uri("https://bot.contoso.com/callback") + }, + }; + } + + private static IEnumerable TestData_CreateOPSCall() + { + return new[] + { + new object?[] + { + new CallInvite(new MicrosoftTeamsAppIdentifier("28:acs:12345")), new Uri("https://bot.contoso.com/callback") }, }; diff --git a/sdk/communication/Azure.Communication.CallAutomation/tests/Infrastructure/CallAutomationTestBase.cs b/sdk/communication/Azure.Communication.CallAutomation/tests/Infrastructure/CallAutomationTestBase.cs index bfcad98bdde7..8b84035f43ab 100644 --- a/sdk/communication/Azure.Communication.CallAutomation/tests/Infrastructure/CallAutomationTestBase.cs +++ b/sdk/communication/Azure.Communication.CallAutomation/tests/Infrastructure/CallAutomationTestBase.cs @@ -34,6 +34,29 @@ public class CallAutomationTestBase "\"mediaSubscriptionId\": {0}," + "\"dataSubscriptionId\": {1}" + "}}"; + protected const string DummyOPSPayload = "{{" + + "\"callConnectionId\": \"someCallConnectionId\"," + + "\"serverCallId\": \"someServerCallId\"," + + "\"targets\": [" + + "{{" + + "\"rawId\":\"targetId\"," + + "\"kind\":\"communicationUser\"," + + "\"communicationUser\":{{\"id\":\"targetId\"}}" + + "}}" + + "]," + + "\"sourceDisplayName\": \"displayName\"," + + "\"source\":{{" + + "\"rawId\":\"sourceId\"," + + "\"kind\":\"microsoftTeamsApp\"," + + "\"microsoftTeamsApp\":{{\"appId\":\"sourceId\"," + + "\"cloud\": \"public\"}}" + + "}}," + + "\"callConnectionState\": \"connecting\"," + + "\"subject\": \"dummySubject\"," + + "\"callbackUri\": \"https://bot.contoso.com/callback\"," + + "\"mediaSubscriptionId\": {0}," + + "\"dataSubscriptionId\": {1}" + + "}}"; protected const string SourceId = "sourceId"; protected const string TargetId = "targetId"; protected const string ServerCallId = "someServerCallId"; @@ -47,9 +70,10 @@ public class CallAutomationTestBase private const string NoneDataSubscriptionId = "null"; private const string DataSubscriptionId = "\"dataSubscriptionId\""; protected string CreateOrAnswerCallOrGetCallConnectionPayload = string.Format(DummyPayload, NoneMediaSubscriptionId, NoneDataSubscriptionId); + protected string CreateOrAnswerCallOrGetCallConnectionPayloadForOPSCall = string.Format(DummyOPSPayload, NoneMediaSubscriptionId, NoneDataSubscriptionId); protected string CreateOrAnswerCallOrGetCallConnectionWithMediaSubscriptionAndTranscriptionPayload = string.Format(DummyPayload, MediaSubscriptionId, DataSubscriptionId); - internal CallAutomationClient CreateMockCallAutomationClient(int responseCode, object? responseContent = null, HttpHeader[]? httpHeaders = null) + internal CallAutomationClient CreateMockCallAutomationClient(int responseCode, object? responseContent = null, HttpHeader[]? httpHeaders = null, bool isOPSCall = false) { var mockResponse = new MockResponse(responseCode); @@ -73,9 +97,13 @@ internal CallAutomationClient CreateMockCallAutomationClient(int responseCode, o } } - var callAutomationClientOptions = new CallAutomationClientOptions() + var callAutomationClientOptions = isOPSCall ? new CallAutomationClientOptions() + { + OPSSource = new MicrosoftTeamsAppIdentifier(SourceId), + Transport = new MockTransport(mockResponse) + } : new CallAutomationClientOptions() { - Source = new CommunicationUserIdentifier("12345"), + Source = new CommunicationUserIdentifier(SourceId), Transport = new MockTransport(mockResponse) }; @@ -105,5 +133,19 @@ protected void verifyCallConnectionProperties(CallConnectionProperties callConne Assert.AreEqual(CallBackUri, callConnectionProperties.CallbackUri.ToString()); Assert.AreEqual(DisplayName, callConnectionProperties.SourceDisplayName); } + + protected void verifyOPSCallConnectionProperties(CallConnectionProperties callConnectionProperties) + { + Assert.AreEqual(CallConnectionId, callConnectionProperties.CallConnectionId); + Assert.AreEqual(ServerCallId, callConnectionProperties.ServerCallId); + var opsSourceUser = (MicrosoftTeamsAppIdentifier)callConnectionProperties.Source; + Assert.AreEqual(SourceId, opsSourceUser.AppId); + Assert.AreEqual(callConnectionProperties.Targets.Count, 1); + var targetUser = (CommunicationUserIdentifier)callConnectionProperties.Targets[0]; + Assert.AreEqual(TargetId, targetUser.Id); + Assert.AreEqual(CallConnectionState.Connecting, callConnectionProperties.CallConnectionState); + Assert.AreEqual(CallBackUri, callConnectionProperties.CallbackUri.ToString()); + Assert.AreEqual(DisplayName, callConnectionProperties.SourceDisplayName); + } } }