diff --git a/sdk/communication/azure-communication-callautomation/assets.json b/sdk/communication/azure-communication-callautomation/assets.json index f9b4280a9b17..e011f2ddd486 100644 --- a/sdk/communication/azure-communication-callautomation/assets.json +++ b/sdk/communication/azure-communication-callautomation/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/communication/azure-communication-callautomation", - "Tag": "java/communication/azure-communication-callautomation_8eed475b32" + "Tag": "java/communication/azure-communication-callautomation_cf67d9f149" } \ No newline at end of file diff --git a/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/CallAutomationAsyncClient.java b/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/CallAutomationAsyncClient.java index 111e66b98f94..ab277b6c9987 100644 --- a/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/CallAutomationAsyncClient.java +++ b/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/CallAutomationAsyncClient.java @@ -398,6 +398,13 @@ Mono> answerCallWithResponseInternal(AnswerCallOption = getTranscriptionOptionsInternal(answerCallOptions.getTranscriptionOptions()); request.setTranscriptionOptions(transcriptionOptionsInternal); } + if (answerCallOptions.getCustomCallingContext().getSipHeaders() != null + || answerCallOptions.getCustomCallingContext().getVoipHeaders() != null) { + CustomCallingContext customContext = new CustomCallingContext(); + customContext.setSipHeaders(answerCallOptions.getCustomCallingContext().getSipHeaders()); + customContext.setVoipHeaders(answerCallOptions.getCustomCallingContext().getVoipHeaders()); + request.setCustomCallingContext(customContext); + } return azureCommunicationCallAutomationServiceInternal.answerCallWithResponseAsync(request, context) .map(response -> { diff --git a/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/models/AnswerCallOptions.java b/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/models/AnswerCallOptions.java index e8ab5c0e92fe..284af397e034 100644 --- a/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/models/AnswerCallOptions.java +++ b/sdk/communication/azure-communication-callautomation/src/main/java/com/azure/communication/callautomation/models/AnswerCallOptions.java @@ -3,6 +3,8 @@ package com.azure.communication.callautomation.models; +import java.util.HashMap; + import com.azure.core.annotation.Fluent; /** @@ -40,6 +42,11 @@ public final class AnswerCallOptions { */ private String operationContext; + /** + * Custom Context + */ + private final CustomCallingContext customCallingContext; + /** * Constructor * @@ -49,6 +56,8 @@ public final class AnswerCallOptions { public AnswerCallOptions(String incomingCallContext, String callbackUrl) { this.incomingCallContext = incomingCallContext; this.callbackUrl = callbackUrl; + this.customCallingContext + = new CustomCallingContext(new HashMap(), new HashMap()); } /** @@ -148,4 +157,12 @@ public AnswerCallOptions setMediaStreamingOptions(MediaStreamingOptions mediaStr this.mediaStreamingOptions = mediaStreamingOptions; return this; } + + /** + * get custom context + * @return custom context + */ + public CustomCallingContext getCustomCallingContext() { + return customCallingContext; + } } diff --git a/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientAutomatedLiveTests.java b/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientAutomatedLiveTests.java index d32417718c58..7b5c107f1c2d 100644 --- a/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientAutomatedLiveTests.java +++ b/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientAutomatedLiveTests.java @@ -213,6 +213,121 @@ public void createVOIPCallAndRejectAutomatedTest(HttpClient httpClient) { } } + @ParameterizedTest + @MethodSource("com.azure.core.test.TestBase#getHttpClients") + @DisabledIfEnvironmentVariable( + named = "SKIP_LIVE_TEST", + matches = "(?i)(true)", + disabledReason = "Requires environment to be set up") + public void createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest(HttpClient httpClient) { + /* Test case: ACS to ACS call + * 1. create a CallAutomationClient. + * 2. create a call from source to one ACS target. + * 3. get updated call properties and check for the connected state. + * 4. hang up the call. + * 5. once call is hung up, verify disconnected event + */ + + CommunicationIdentityAsyncClient identityAsyncClient + = getCommunicationIdentityClientUsingConnectionString(httpClient).addPolicy( + (context, next) -> logHeaders("createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest", next)) + .buildAsyncClient(); + + List callDestructors = new ArrayList<>(); + + try { + // create caller and receiver + CommunicationUserIdentifier caller = identityAsyncClient.createUser().block(); + CommunicationIdentifier target = identityAsyncClient.createUser().block(); + + // Create call automation client and use source as the caller. + CallAutomationAsyncClient callerAsyncClient = getCallAutomationClientUsingConnectionString(httpClient) + .addPolicy((context, + next) -> logHeaders("createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest", next)) + .sourceIdentity(caller) + .buildAsyncClient(); + // Create call automation client for receivers. + CallAutomationAsyncClient receiverAsyncClient = getCallAutomationClientUsingConnectionString(httpClient) + .addPolicy((context, + next) -> logHeaders("createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest", next)) + .buildAsyncClient(); + + String uniqueId = serviceBusWithNewCall(caller, target); + + // create a call + List targets = new ArrayList<>(Collections.singletonList(target)); + CreateGroupCallOptions createCallOptions + = new CreateGroupCallOptions(targets, DISPATCHER_CALLBACK + String.format("?q=%s", uniqueId)); + Response createCallResultResponse + = callerAsyncClient.createGroupCallWithResponse(createCallOptions).block(); + + assertNotNull(createCallResultResponse); + CreateCallResult createCallResult = createCallResultResponse.getValue(); + assertNotNull(createCallResult); + assertNotNull(createCallResult.getCallConnectionProperties()); + String callerConnectionId = createCallResult.getCallConnectionProperties().getCallConnectionId(); + assertNotNull(callerConnectionId); + + // wait for the incomingCallContext + String incomingCallContext = waitForIncomingCallContext(uniqueId, Duration.ofSeconds(10)); + assertNotNull(incomingCallContext); + + // answer the call + AnswerCallOptions answerCallOptions + = new AnswerCallOptions(incomingCallContext, DISPATCHER_CALLBACK + String.format("?q=%s", uniqueId)); + answerCallOptions.getCustomCallingContext().addSipUui("OBOuuivalue"); + answerCallOptions.getCustomCallingContext().addSipX("XheaderOBO", "value"); + + AnswerCallResult answerCallResult + = Objects.requireNonNull(receiverAsyncClient.answerCallWithResponse(answerCallOptions).block()) + .getValue(); + assertNotNull(answerCallResult); + assertNotNull(answerCallResult.getCallConnectionAsync()); + assertNotNull(answerCallResult.getCallConnectionProperties()); + String receiverConnectionId = answerCallResult.getCallConnectionProperties().getCallConnectionId(); + callDestructors.add(answerCallResult.getCallConnectionAsync()); + + // check events to caller side + CallConnected callerCallConnected + = waitForEvent(CallConnected.class, callerConnectionId, Duration.ofSeconds(10)); + ParticipantsUpdated callerParticipantUpdatedEvent + = waitForEvent(ParticipantsUpdated.class, callerConnectionId, Duration.ofSeconds(10)); + assertNotNull(callerCallConnected); + assertNotNull(callerParticipantUpdatedEvent); + + // check events to receiver side + CallConnected receiverCallConnected + = waitForEvent(CallConnected.class, receiverConnectionId, Duration.ofSeconds(10)); + ParticipantsUpdated receiverParticipantUpdatedEvent + = waitForEvent(ParticipantsUpdated.class, callerConnectionId, Duration.ofSeconds(10)); + assertNotNull(receiverCallConnected); + assertNotNull(receiverParticipantUpdatedEvent); + + // hang up the call. + answerCallResult.getCallConnectionAsync().hangUp(true).block(); + + // check if both parties had the call terminated. + CallDisconnected callerCallDisconnected + = waitForEvent(CallDisconnected.class, receiverConnectionId, Duration.ofSeconds(10)); + CallDisconnected receiverCallDisconnected + = waitForEvent(CallDisconnected.class, callerConnectionId, Duration.ofSeconds(10)); + assertNotNull(callerCallDisconnected); + assertNotNull(receiverCallDisconnected); + + } catch (Exception ex) { + fail("Unexpected exception received", ex); + } finally { + if (!callDestructors.isEmpty()) { + try { + callDestructors.forEach(callConnection -> callConnection.hangUpWithResponse(true).block()); + } catch (Exception ignored) { + // Some call might have been terminated during the test, and it will cause exceptions here. + // Do nothing and iterate to next call connection. + } + } + } + } + @ParameterizedTest @MethodSource("com.azure.core.test.TestBase#getHttpClients") @DisabledIfEnvironmentVariable( diff --git a/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientUnitTests.java b/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientUnitTests.java index b44f72d2f7a1..b3148f6c8baa 100644 --- a/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientUnitTests.java +++ b/sdk/communication/azure-communication-callautomation/src/test/java/com/azure/communication/callautomation/CallAutomationAsyncClientUnitTests.java @@ -112,6 +112,25 @@ public void answerCall() { assertNotNull(answerCallResult); } + @Test + public void answerCallWithResponseAndCustomContext() { + CallAutomationAsyncClient callAutomationAsyncClient = getCallAutomationAsyncClient(new ArrayList<>( + Collections.singletonList(new AbstractMap.SimpleEntry<>(generateCallProperties(CALL_CONNECTION_ID, + CALL_SERVER_CALL_ID, CALL_CALLER_ID, CALL_CALLER_DISPLAY_NAME, CALL_TARGET_ID, CALL_CONNECTION_STATE, + CALL_SUBJECT, CALL_CALLBACK_URL, null, null), 200)))); + + AnswerCallOptions answerCallOptions = new AnswerCallOptions(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL); + answerCallOptions.getCustomCallingContext().addSipUui("OBOuuivalue"); + answerCallOptions.getCustomCallingContext().addSipX("XheaderOBO", "value"); + + Response answerCallResult + = callAutomationAsyncClient.answerCallWithResponse(answerCallOptions).block(); + + assertNotNull(answerCallResult); + assertEquals(200, answerCallResult.getStatusCode()); + assertNotNull(answerCallResult.getValue()); + } + @Test public void answerCallWithResponse() { CallAutomationAsyncClient callAutomationAsyncClient = getCallAutomationAsyncClient(new ArrayList<>( diff --git a/sdk/communication/azure-communication-callautomation/src/test/resources/createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest.json b/sdk/communication/azure-communication-callautomation/src/test/resources/createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest.json new file mode 100644 index 000000000000..b8311df376ee --- /dev/null +++ b/sdk/communication/azure-communication-callautomation/src/test/resources/createVOIPCallAndAnswerWithCustomContextThenHangupAutomatedTest.json @@ -0,0 +1 @@ +["{\"to\":{\"kind\":\"communicationUser\",\"rawId\":\"REDACTED\",\"communicationUser\":{\"id\":\"REDACTED\"}},\"from\":{\"kind\":\"communicationUser\",\"rawId\":\"REDACTED\",\"communicationUser\":{\"id\":\"REDACTED\"}},\"serverCallId\":\"REDACTED\",\"callerDisplayName\":\"\",\"incomingCallContext\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\"}","[{\"id\":\"REDACTED\",\"source\":\"calling/callConnections/24002180-b059-4741-a392-a1aed1126925\",\"type\":\"Microsoft.Communication.CallConnected\",\"data\":{\"version\":\"2024-09-01-preview\",\"resultInformation\":{\"code\":200,\"subCode\":0,\"message\":\"\"},\"callConnectionId\":\"24002180-b059-4741-a392-a1aed1126925\",\"serverCallId\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\",\"publicEventType\":\"Microsoft.Communication.CallConnected\"},\"time\":\"2024-12-27T21:04:58.0090118\\u002B00:00\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"subject\":\"calling/callConnections/24002180-b059-4741-a392-a1aed1126925\"}]","[{\"id\":\"REDACTED\",\"source\":\"calling/callConnections/24002180-2951-4e61-aace-194bbd10bbb1\",\"type\":\"Microsoft.Communication.ParticipantsUpdated\",\"data\":{\"participants\":[{\"identifier\":{\"rawId\":\"REDACTED\",\"kind\":\"communicationUser\",\"communicationUser\":{\"id\":\"REDACTED\"}},\"isMuted\":false,\"isOnHold\":false},{\"identifier\":{\"rawId\":\"REDACTED\",\"kind\":\"communicationUser\",\"communicationUser\":{\"id\":\"REDACTED\"}},\"isMuted\":false,\"isOnHold\":false}],\"sequenceNumber\":1,\"resultInformation\":{\"code\":200,\"subCode\":0,\"message\":\"\"},\"version\":\"2024-09-01-preview\",\"callConnectionId\":\"24002180-2951-4e61-aace-194bbd10bbb1\",\"serverCallId\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\",\"publicEventType\":\"Microsoft.Communication.ParticipantsUpdated\"},\"time\":\"2024-12-27T21:04:58.0121227\\u002B00:00\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"subject\":\"calling/callConnections/24002180-2951-4e61-aace-194bbd10bbb1\"}]","[{\"id\":\"REDACTED\",\"source\":\"calling/callConnections/24002180-2951-4e61-aace-194bbd10bbb1\",\"type\":\"Microsoft.Communication.CallConnected\",\"data\":{\"version\":\"2024-09-01-preview\",\"resultInformation\":{\"code\":200,\"subCode\":0,\"message\":\"\"},\"callConnectionId\":\"24002180-2951-4e61-aace-194bbd10bbb1\",\"serverCallId\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\",\"publicEventType\":\"Microsoft.Communication.CallConnected\"},\"time\":\"2024-12-27T21:04:58.0090118\\u002B00:00\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"subject\":\"calling/callConnections/24002180-2951-4e61-aace-194bbd10bbb1\"}]","[{\"id\":\"REDACTED\",\"source\":\"calling/callConnections/24002180-b059-4741-a392-a1aed1126925\",\"type\":\"Microsoft.Communication.ParticipantsUpdated\",\"data\":{\"participants\":[{\"identifier\":{\"rawId\":\"REDACTED\",\"kind\":\"communicationUser\",\"communicationUser\":{\"id\":\"REDACTED\"}},\"isMuted\":false,\"isOnHold\":false},{\"identifier\":{\"rawId\":\"REDACTED\",\"kind\":\"communicationUser\",\"communicationUser\":{\"id\":\"REDACTED\"}},\"isMuted\":false,\"isOnHold\":false}],\"sequenceNumber\":1,\"resultInformation\":{\"code\":200,\"subCode\":0,\"message\":\"\"},\"version\":\"2024-09-01-preview\",\"callConnectionId\":\"24002180-b059-4741-a392-a1aed1126925\",\"serverCallId\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\",\"publicEventType\":\"Microsoft.Communication.ParticipantsUpdated\"},\"time\":\"2024-12-27T21:04:58.0121227\\u002B00:00\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"subject\":\"calling/callConnections/24002180-b059-4741-a392-a1aed1126925\"}]","[{\"id\":\"REDACTED\",\"source\":\"calling/callConnections/24002180-2951-4e61-aace-194bbd10bbb1\",\"type\":\"Microsoft.Communication.CallDisconnected\",\"data\":{\"version\":\"2024-09-01-preview\",\"resultInformation\":{\"code\":200,\"subCode\":7000,\"message\":\"The conversation has ended. DiagCode: 0#7000.@\"},\"callConnectionId\":\"24002180-2951-4e61-aace-194bbd10bbb1\",\"serverCallId\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\",\"publicEventType\":\"Microsoft.Communication.CallDisconnected\"},\"time\":\"2024-12-27T21:04:59.0901871\\u002B00:00\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"subject\":\"calling/callConnections/24002180-2951-4e61-aace-194bbd10bbb1\"}]","[{\"id\":\"REDACTED\",\"source\":\"calling/callConnections/24002180-b059-4741-a392-a1aed1126925\",\"type\":\"Microsoft.Communication.CallDisconnected\",\"data\":{\"version\":\"2024-09-01-preview\",\"resultInformation\":{\"code\":200,\"subCode\":5001,\"message\":\"This conversation has ended.. DiagCode: 0#5001.@\"},\"callConnectionId\":\"24002180-b059-4741-a392-a1aed1126925\",\"serverCallId\":\"REDACTED\",\"correlationId\":\"dd7b828d-5f8b-43d4-be3f-d95c675f19ca\",\"publicEventType\":\"Microsoft.Communication.CallDisconnected\"},\"time\":\"2024-12-27T21:04:59.3246749\\u002B00:00\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"subject\":\"calling/callConnections/24002180-b059-4741-a392-a1aed1126925\"}]"] \ No newline at end of file