From 5f7921ffb27e5adc7ee1b6bd2d39d62cb8552db7 Mon Sep 17 00:00:00 2001 From: Pilli Vamshi Date: Wed, 4 Dec 2024 18:06:29 +0530 Subject: [PATCH 1/4] Added custom calling context in answer api. --- .../communication-call-automation/src/callAutomationClient.ts | 3 +++ .../communication-call-automation/src/models/options.ts | 2 ++ 2 files changed, 5 insertions(+) diff --git a/sdk/communication/communication-call-automation/src/callAutomationClient.ts b/sdk/communication/communication-call-automation/src/callAutomationClient.ts index e869021cdd24..4ddc27290eb6 100644 --- a/sdk/communication/communication-call-automation/src/callAutomationClient.ts +++ b/sdk/communication/communication-call-automation/src/callAutomationClient.ts @@ -334,6 +334,9 @@ export class CallAutomationClient { operationContext: operationContext, callbackUri: callbackUrl, answeredBy: this.sourceIdentity, + customCallingContext: this.createCustomCallingContextInternal( + options.customCallingContext!, + ), }; const optionsInternal = { ...operationOptions, diff --git a/sdk/communication/communication-call-automation/src/models/options.ts b/sdk/communication/communication-call-automation/src/models/options.ts index 17d23a535b70..3dbc9f0b8188 100644 --- a/sdk/communication/communication-call-automation/src/models/options.ts +++ b/sdk/communication/communication-call-automation/src/models/options.ts @@ -130,6 +130,8 @@ export interface AnswerCallOptions extends OperationOptions { transcriptionConfiguration?: TranscriptionConfiguration; /** The operation context. */ operationContext?: string; + /** The Custom Context. */ + customCallingContext?: CustomCallingContext; } /** From 11be23eb0bb906797bc2a365160e29fcaf2ef91a Mon Sep 17 00:00:00 2001 From: Pilli Vamshi Date: Thu, 5 Dec 2024 19:01:42 +0530 Subject: [PATCH 2/4] Added live and unit tests. --- .../node/callAutomationClient.live.spec.ts | 96 +++++++++++++++++++ .../test/node/callAutomationClient.spec.ts | 26 ++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts b/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts index 6290834543f9..e1b673439088 100644 --- a/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts +++ b/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts @@ -112,6 +112,54 @@ describe("Call Automation Main Client Live Tests", { skip: !isNodeLike }, () => assert.isDefined(callDisconnectedEvent); }); + it("answer call with custom context and hangup", { timeout: 60000 }, async (ctx) => { + const fullTitle: string | undefined = + ctx.task.suite && ctx.task.suite.name && ctx.task.name + ? `${ctx.task.suite.name} ${ctx.task.name}` + : undefined; + + testName = fullTitle ? fullTitle.replace(/ /g, "_") : "answer_call_with_custom_context_and_hang_up"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "operationContextCreateCall" }; + + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + const answerCallOptions: AnswerCallOptions = { + operationContext: "operationContextAnswerCall", + customCallingContext : [{ kind: "voip", key: "foo", value: "bar" }] + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOptions, + ); + } + + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + await callConnection.hangUp(true); + + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }); + it("Reject call", { timeout: 60000 }, async (ctx) => { const fullTitle: string | undefined = ctx.task.suite && ctx.task.suite.name && ctx.task.name @@ -143,4 +191,52 @@ describe("Call Automation Main Client Live Tests", { skip: !isNodeLike }, () => const createCallFailedEvent = await waitForEvent("CreateCallFailed", callConnectionId, 8000); assert.isDefined(createCallFailedEvent); }); + + it("answer call with custom context and hangup", { timeout: 60000 }, async (ctx) => { + const fullTitle: string | undefined = + ctx.task.suite && ctx.task.suite.name && ctx.task.name + ? `${ctx.task.suite.name} ${ctx.task.name}` + : undefined; + + testName = fullTitle ? fullTitle.replace(/ /g, "_") : "create_call_and_hang_up"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "operationContextCreateCall" }; + + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + const answerCallOptions: AnswerCallOptions = { + operationContext: "operationContextAnswerCall", + customCallingContext = CustomCallingContextInternal[] + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOptions, + ); + } + + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + await callConnection.hangUp(true); + + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }); }); diff --git a/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts b/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts index bced07a5ff99..b53b4f67f51a 100644 --- a/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts +++ b/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts @@ -13,7 +13,7 @@ import type { CommunicationIdentifier, MicrosoftTeamsAppIdentifier, } from "@azure/communication-common"; -import type { CallInvite, CallConnection } from "../../src/index.js"; +import type { CallInvite, CallConnection, AnswerCallOptions } from "../../src/index.js"; import type { AnswerCallEventResult, CreateCallEventResult, @@ -198,6 +198,30 @@ describe("Call Automation Client Unit Tests", () => { assert.equal(result, answerCallResultMock); }); + it("AnswerCall with custom context", async () => { + // mocks + const answerCallResultMock: AnswerCallResult = { + callConnectionProperties: {} as CallConnectionProperties, + callConnection: {} as CallConnection, + waitForEventProcessor: async () => { + return {} as AnswerCallEventResult; + }, + }; + vi.spyOn(client, "answerCall").mockResolvedValue(answerCallResultMock); + const answerCallOptions: AnswerCallOptions = { + operationContext: "operationContextAnswerCall", + customCallingContext : [{ kind: "voip", key: "foo", value: "bar" }] + }; + const promiseResult = client.answerCall(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL, answerCallOptions); + + // asserts + const result = await promiseResult; + + assert.isNotNull(result); + expect(client.answerCall).toHaveBeenCalledWith(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL); + assert.equal(result, answerCallResultMock); + }); + it("RedirectCall", async () => { // mocks vi.spyOn(client, "redirectCall").mockReturnValue( From c9759759d0e01fd5aaf2bedddd106164855a2e7b Mon Sep 17 00:00:00 2001 From: Pilli Vamshi Date: Fri, 17 Jan 2025 17:59:44 +0530 Subject: [PATCH 3/4] Removed live test. --- .../node/callAutomationClient.live.spec.ts | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts b/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts index e1b673439088..a96e6c6feba2 100644 --- a/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts +++ b/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts @@ -191,52 +191,4 @@ describe("Call Automation Main Client Live Tests", { skip: !isNodeLike }, () => const createCallFailedEvent = await waitForEvent("CreateCallFailed", callConnectionId, 8000); assert.isDefined(createCallFailedEvent); }); - - it("answer call with custom context and hangup", { timeout: 60000 }, async (ctx) => { - const fullTitle: string | undefined = - ctx.task.suite && ctx.task.suite.name && ctx.task.name - ? `${ctx.task.suite.name} ${ctx.task.name}` - : undefined; - - testName = fullTitle ? fullTitle.replace(/ /g, "_") : "create_call_and_hang_up"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "operationContextCreateCall" }; - - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - const answerCallOptions: AnswerCallOptions = { - operationContext: "operationContextAnswerCall", - customCallingContext = CustomCallingContextInternal[] - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOptions, - ); - } - - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - await callConnection.hangUp(true); - - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }); }); From 079708344a5922f0f99b038d72efe276bb48ec36 Mon Sep 17 00:00:00 2001 From: Pilli Vamshi Date: Thu, 30 Jan 2025 15:32:58 +0530 Subject: [PATCH 4/4] recorded the live test --- .../communication-call-automation/assets.json | 2 +- ...r_call_with_custom_context_and_hangup.json | 190 ++++++++++++++++++ .../src/callAutomationClient.ts | 3 - .../src/models/options.ts | 2 - .../node/callAutomationClient.live.spec.ts | 6 +- .../test/node/callAutomationClient.spec.ts | 8 +- 6 files changed, 201 insertions(+), 10 deletions(-) create mode 100644 sdk/communication/communication-call-automation/recordings/Call_Automation_Main_Client_Live_Tests_answer_call_with_custom_context_and_hangup.json diff --git a/sdk/communication/communication-call-automation/assets.json b/sdk/communication/communication-call-automation/assets.json index 12cf6f0e94bd..d72ef4cd7cee 100644 --- a/sdk/communication/communication-call-automation/assets.json +++ b/sdk/communication/communication-call-automation/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "js", "TagPrefix": "js/communication/communication-call-automation", - "Tag": "js/communication/communication-call-automation_80bab71cb3" + "Tag": "js/communication/communication-call-automation_17439279fc" } diff --git a/sdk/communication/communication-call-automation/recordings/Call_Automation_Main_Client_Live_Tests_answer_call_with_custom_context_and_hangup.json b/sdk/communication/communication-call-automation/recordings/Call_Automation_Main_Client_Live_Tests_answer_call_with_custom_context_and_hangup.json new file mode 100644 index 000000000000..bafc8d1a5bd9 --- /dev/null +++ b/sdk/communication/communication-call-automation/recordings/Call_Automation_Main_Client_Live_Tests_answer_call_with_custom_context_and_hangup.json @@ -0,0 +1,190 @@ +[ + { + "to": { + "kind": "communicationUser", + "rawId": "sanitized", + "communicationUser": { + "id": "sanitized" + } + }, + "from": { + "kind": "communicationUser", + "rawId": "sanitized", + "communicationUser": { + "id": "sanitized" + } + }, + "serverCallId": "sanitized", + "callerDisplayName": "", + "incomingCallContext": "sanitized", + "correlationId": "57029fdd-4530-44cc-a721-a742735dc478" + }, + [ + { + "id": "sanitized", + "source": "calling/callConnections/01002680-4199-49c4-abd7-d59a5600111b", + "type": "Microsoft.Communication.ParticipantsUpdated", + "data": { + "participants": [ + { + "identifier": { + "rawId": "sanitized", + "kind": "communicationUser", + "communicationUser": { + "id": "sanitized" + } + }, + "isMuted": false, + "isOnHold": false + }, + { + "identifier": { + "rawId": "sanitized", + "kind": "communicationUser", + "communicationUser": { + "id": "sanitized" + } + }, + "isMuted": false, + "isOnHold": false + } + ], + "sequenceNumber": 1, + "resultInformation": { + "code": 200, + "subCode": 0, + "message": "" + }, + "version": "2024-09-01-preview", + "callConnectionId": "01002680-4199-49c4-abd7-d59a5600111b", + "serverCallId": "sanitized", + "correlationId": "57029fdd-4530-44cc-a721-a742735dc478", + "publicEventType": "Microsoft.Communication.ParticipantsUpdated" + }, + "time": "2025-01-30T09:19:14.8562282+00:00", + "specversion": "1.0", + "datacontenttype": "application/json", + "subject": "calling/callConnections/01002680-4199-49c4-abd7-d59a5600111b" + } + ], + [ + { + "id": "sanitized", + "source": "calling/callConnections/01002680-4199-49c4-abd7-d59a5600111b", + "type": "Microsoft.Communication.CallConnected", + "data": { + "version": "2024-09-01-preview", + "operationContext": "operationContextCreateCall", + "resultInformation": { + "code": 200, + "subCode": 0, + "message": "" + }, + "callConnectionId": "01002680-4199-49c4-abd7-d59a5600111b", + "serverCallId": "sanitized", + "correlationId": "57029fdd-4530-44cc-a721-a742735dc478", + "publicEventType": "Microsoft.Communication.CallConnected" + }, + "time": "2025-01-30T09:19:14.9400822+00:00", + "specversion": "1.0", + "datacontenttype": "application/json", + "subject": "calling/callConnections/01002680-4199-49c4-abd7-d59a5600111b" + } + ], + [ + { + "id": "sanitized", + "source": "calling/callConnections/03002680-db5e-48b3-9598-64b333125067", + "type": "Microsoft.Communication.CallConnected", + "data": { + "version": "2024-09-01-preview", + "operationContext": "operationContextAnswerCall", + "resultInformation": { + "code": 200, + "subCode": 0, + "message": "" + }, + "callConnectionId": "03002680-db5e-48b3-9598-64b333125067", + "serverCallId": "sanitized", + "correlationId": "57029fdd-4530-44cc-a721-a742735dc478", + "publicEventType": "Microsoft.Communication.CallConnected" + }, + "time": "2025-01-30T09:19:14.9022403+00:00", + "specversion": "1.0", + "datacontenttype": "application/json", + "subject": "calling/callConnections/03002680-db5e-48b3-9598-64b333125067" + } + ], + [ + { + "id": "sanitized", + "source": "calling/callConnections/03002680-db5e-48b3-9598-64b333125067", + "type": "Microsoft.Communication.ParticipantsUpdated", + "data": { + "participants": [ + { + "identifier": { + "rawId": "sanitized", + "kind": "communicationUser", + "communicationUser": { + "id": "sanitized" + } + }, + "isMuted": false, + "isOnHold": false + }, + { + "identifier": { + "rawId": "sanitized", + "kind": "communicationUser", + "communicationUser": { + "id": "sanitized" + } + }, + "isMuted": false, + "isOnHold": false + } + ], + "sequenceNumber": 1, + "resultInformation": { + "code": 200, + "subCode": 0, + "message": "" + }, + "version": "2024-09-01-preview", + "callConnectionId": "03002680-db5e-48b3-9598-64b333125067", + "serverCallId": "sanitized", + "correlationId": "57029fdd-4530-44cc-a721-a742735dc478", + "publicEventType": "Microsoft.Communication.ParticipantsUpdated" + }, + "time": "2025-01-30T09:19:14.8724523+00:00", + "specversion": "1.0", + "datacontenttype": "application/json", + "subject": "calling/callConnections/03002680-db5e-48b3-9598-64b333125067" + } + ], + [ + { + "id": "sanitized", + "source": "calling/callConnections/01002680-4199-49c4-abd7-d59a5600111b", + "type": "Microsoft.Communication.CallDisconnected", + "data": { + "version": "2024-09-01-preview", + "operationContext": "operationContextCreateCall", + "resultInformation": { + "code": 200, + "subCode": 7000, + "message": "The conversation has ended. DiagCode: 0#7000.@" + }, + "callConnectionId": "01002680-4199-49c4-abd7-d59a5600111b", + "serverCallId": "sanitized", + "correlationId": "57029fdd-4530-44cc-a721-a742735dc478", + "publicEventType": "Microsoft.Communication.CallDisconnected" + }, + "time": "2025-01-30T09:19:16.6512479+00:00", + "specversion": "1.0", + "datacontenttype": "application/json", + "subject": "calling/callConnections/01002680-4199-49c4-abd7-d59a5600111b" + } + ] +] \ No newline at end of file diff --git a/sdk/communication/communication-call-automation/src/callAutomationClient.ts b/sdk/communication/communication-call-automation/src/callAutomationClient.ts index f48b0114dbc5..a22a8b066d8f 100644 --- a/sdk/communication/communication-call-automation/src/callAutomationClient.ts +++ b/sdk/communication/communication-call-automation/src/callAutomationClient.ts @@ -339,9 +339,6 @@ export class CallAutomationClient { operationContext: operationContext, callbackUri: callbackUrl, answeredBy: this.sourceIdentity, - customCallingContext: this.createCustomCallingContextInternal( - options.customCallingContext!, - ), }; const optionsInternal = { ...operationOptions, diff --git a/sdk/communication/communication-call-automation/src/models/options.ts b/sdk/communication/communication-call-automation/src/models/options.ts index fed1efb4f3d8..e0ea30e60516 100644 --- a/sdk/communication/communication-call-automation/src/models/options.ts +++ b/sdk/communication/communication-call-automation/src/models/options.ts @@ -144,8 +144,6 @@ export interface AnswerCallOptions extends OperationOptions { transcriptionOptions?: TranscriptionOptions; /** The operation context. */ operationContext?: string; - /** The Custom Context. */ - customCallingContext?: CustomCallingContext; } /** diff --git a/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts b/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts index a96e6c6feba2..75199997f183 100644 --- a/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts +++ b/sdk/communication/communication-call-automation/test/node/callAutomationClient.live.spec.ts @@ -118,7 +118,9 @@ describe("Call Automation Main Client Live Tests", { skip: !isNodeLike }, () => ? `${ctx.task.suite.name} ${ctx.task.name}` : undefined; - testName = fullTitle ? fullTitle.replace(/ /g, "_") : "answer_call_with_custom_context_and_hang_up"; + testName = fullTitle + ? fullTitle.replace(/ /g, "_") + : "answer_call_with_custom_context_and_hang_up"; await loadPersistedEvents(testName); const callInvite: CallInvite = { targetParticipant: testUser2 }; @@ -140,7 +142,7 @@ describe("Call Automation Main Client Live Tests", { skip: !isNodeLike }, () => if (incomingCallContext) { const answerCallOptions: AnswerCallOptions = { operationContext: "operationContextAnswerCall", - customCallingContext : [{ kind: "voip", key: "foo", value: "bar" }] + customCallingContext: [{ kind: "voip", key: "foo", value: "bar" }], }; await receiverCallAutomationClient.answerCall( incomingCallContext, diff --git a/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts b/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts index bd32d8252359..8792de357abf 100644 --- a/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts +++ b/sdk/communication/communication-call-automation/test/node/callAutomationClient.spec.ts @@ -197,9 +197,13 @@ describe("Call Automation Client Unit Tests", () => { vi.spyOn(client, "answerCall").mockResolvedValue(answerCallResultMock); const answerCallOptions: AnswerCallOptions = { operationContext: "operationContextAnswerCall", - customCallingContext : [{ kind: "voip", key: "foo", value: "bar" }] + customCallingContext: [{ kind: "voip", key: "foo", value: "bar" }], }; - const promiseResult = client.answerCall(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL, answerCallOptions); + const promiseResult = client.answerCall( + CALL_INCOMING_CALL_CONTEXT, + CALL_CALLBACK_URL, + answerCallOptions, + ); // asserts const result = await promiseResult;