diff --git a/sdk/communication/communication-call-automation/src/callMedia.ts b/sdk/communication/communication-call-automation/src/callMedia.ts index 5fff8a1b4a35..a1c68205c1cd 100644 --- a/sdk/communication/communication-call-automation/src/callMedia.ts +++ b/sdk/communication/communication-call-automation/src/callMedia.ts @@ -2,56 +2,56 @@ // Licensed under the MIT License. import { - PlayRequest, - PlaySourceInternal, - FileSourceInternal, - TextSourceInternal, - SsmlSourceInternal, - KnownPlaySourceType, - RecognizeRequest, - KnownRecognizeInputType, - RecognizeOptions, - DtmfOptions, - CallAutomationApiClient, - CallAutomationApiClientOptionalParams, - ContinuousDtmfRecognitionRequest, - SendDtmfTonesRequest, - Tone, - SpeechOptions, - StartTranscriptionRequest, - StopTranscriptionRequest, - UpdateTranscriptionRequest, - HoldRequest, - UnholdRequest, - StopMediaStreamingRequest, - StartMediaStreamingRequest, + PlayRequest, + PlaySourceInternal, + FileSourceInternal, + TextSourceInternal, + SsmlSourceInternal, + KnownPlaySourceType, + RecognizeRequest, + KnownRecognizeInputType, + RecognizeOptions, + DtmfOptions, + CallAutomationApiClient, + CallAutomationApiClientOptionalParams, + ContinuousDtmfRecognitionRequest, + SendDtmfTonesRequest, + Tone, + SpeechOptions, + StartTranscriptionRequest, + StopTranscriptionRequest, + UpdateTranscriptionRequest, + HoldRequest, + UnholdRequest, + StopMediaStreamingRequest, + StartMediaStreamingRequest, } from "./generated/src"; import { CallMediaImpl } from "./generated/src/operations"; import { - CommunicationIdentifier, - createCommunicationAuthPolicy, - serializeCommunicationIdentifier, + CommunicationIdentifier, + createCommunicationAuthPolicy, + serializeCommunicationIdentifier, } from "@azure/communication-common"; import { FileSource, TextSource, SsmlSource, DtmfTone } from "./models/models"; import { - PlayOptions, - PlayToAllOptions, - CallMediaRecognizeDtmfOptions, - CallMediaRecognizeChoiceOptions, - ContinuousDtmfRecognitionOptions, - SendDtmfTonesOptions, - CallMediaRecognizeSpeechOptions, - CallMediaRecognizeSpeechOrDtmfOptions, - StartTranscriptionOptions, - StopTranscriptionOptions, - HoldOptions, - UnholdOptions, - StopMediaStreamingOptions, - StartMediaStreamingOptions, - UpdateTranscriptionOptions, + PlayOptions, + PlayToAllOptions, + CallMediaRecognizeDtmfOptions, + CallMediaRecognizeChoiceOptions, + ContinuousDtmfRecognitionOptions, + SendDtmfTonesOptions, + CallMediaRecognizeSpeechOptions, + CallMediaRecognizeSpeechOrDtmfOptions, + StartTranscriptionOptions, + StopTranscriptionOptions, + HoldOptions, + UnholdOptions, + StopMediaStreamingOptions, + StartMediaStreamingOptions, + UpdateTranscriptionOptions, } from "./models/options"; import { KeyCredential, TokenCredential } from "@azure/core-auth"; import { SendDtmfTonesResult } from "./models/responses"; @@ -61,510 +61,510 @@ import { randomUUID } from "@azure/core-util"; * CallMedia class represents call media related APIs. */ export class CallMedia { - private readonly callConnectionId: string; - private readonly callMedia: CallMediaImpl; - private readonly callAutomationApiClient: CallAutomationApiClient; - constructor( - callConnectionId: string, - endpoint: string, - credential: KeyCredential | TokenCredential, - options?: CallAutomationApiClientOptionalParams, - ) { - this.callAutomationApiClient = new CallAutomationApiClient(endpoint, options); - const authPolicy = createCommunicationAuthPolicy(credential); - this.callAutomationApiClient.pipeline.addPolicy(authPolicy); - this.callConnectionId = callConnectionId; - this.callMedia = new CallMediaImpl(this.callAutomationApiClient); - } + private readonly callConnectionId: string; + private readonly callMedia: CallMediaImpl; + private readonly callAutomationApiClient: CallAutomationApiClient; + constructor( + callConnectionId: string, + endpoint: string, + credential: KeyCredential | TokenCredential, + options?: CallAutomationApiClientOptionalParams, + ) { + this.callAutomationApiClient = new CallAutomationApiClient(endpoint, options); + const authPolicy = createCommunicationAuthPolicy(credential); + this.callAutomationApiClient.pipeline.addPolicy(authPolicy); + this.callConnectionId = callConnectionId; + this.callMedia = new CallMediaImpl(this.callAutomationApiClient); + } - private createPlaySourceInternal( - playSource: FileSource | TextSource | SsmlSource, - ): PlaySourceInternal { - if (playSource.kind === "fileSource") { - const fileSource: FileSourceInternal = { - uri: playSource.url, - }; - return { - kind: KnownPlaySourceType.File, - file: fileSource, - playSourceCacheId: playSource.playsourcacheid, - }; - } else if (playSource.kind === "textSource") { - const textSource: TextSourceInternal = { - text: playSource.text, - sourceLocale: playSource.sourceLocale, - voiceKind: playSource.voiceKind, - voiceName: playSource.voiceName, - customVoiceEndpointId: playSource.customVoiceEndpointId, - }; - return { - kind: KnownPlaySourceType.Text, - text: textSource, - playSourceCacheId: playSource.playsourcacheid, - }; - } else if (playSource.kind === "ssmlSource") { - const ssmlSource: SsmlSourceInternal = { - ssmlText: playSource.ssmlText, - customVoiceEndpointId: playSource.customVoiceEndpointId, - }; - return { - kind: KnownPlaySourceType.Ssml, - ssml: ssmlSource, - playSourceCacheId: playSource.playsourcacheid, - }; - } - throw new Error("Invalid play source"); + private createPlaySourceInternal( + playSource: FileSource | TextSource | SsmlSource, + ): PlaySourceInternal { + if (playSource.kind === "fileSource") { + const fileSource: FileSourceInternal = { + uri: playSource.url, + }; + return { + kind: KnownPlaySourceType.File, + file: fileSource, + playSourceCacheId: playSource.playsourcacheid, + }; + } else if (playSource.kind === "textSource") { + const textSource: TextSourceInternal = { + text: playSource.text, + sourceLocale: playSource.sourceLocale, + voiceKind: playSource.voiceKind, + voiceName: playSource.voiceName, + customVoiceEndpointId: playSource.customVoiceEndpointId, + }; + return { + kind: KnownPlaySourceType.Text, + text: textSource, + playSourceCacheId: playSource.playsourcacheid, + }; + } else if (playSource.kind === "ssmlSource") { + const ssmlSource: SsmlSourceInternal = { + ssmlText: playSource.ssmlText, + customVoiceEndpointId: playSource.customVoiceEndpointId, + }; + return { + kind: KnownPlaySourceType.Ssml, + ssml: ssmlSource, + playSourceCacheId: playSource.playsourcacheid, + }; } + throw new Error("Invalid play source"); + } - /** - * Play audio to a specific participant. - * - * @param playSources - A PlaySource representing the sources to play. Currently only single play source per request is supported. - * @param playTo - The targets to play to. - * @param options - Additional attributes for play. - */ - public async play( - playSources: (FileSource | TextSource | SsmlSource)[], - playTo: CommunicationIdentifier[], - options: PlayOptions = { loop: false }, - ): Promise { - const playRequest: PlayRequest = { - playSources: playSources.map((source) => this.createPlaySourceInternal(source)), - playTo: playTo.map((identifier) => serializeCommunicationIdentifier(identifier)), - playOptions: { - loop: false, - }, - operationContext: options.operationContext, - operationCallbackUri: options.operationCallbackUrl, - }; + /** + * Play audio to a specific participant. + * + * @param playSources - A PlaySource representing the sources to play. Currently only single play source per request is supported. + * @param playTo - The targets to play to. + * @param options - Additional attributes for play. + */ + public async play( + playSources: (FileSource | TextSource | SsmlSource)[], + playTo: CommunicationIdentifier[], + options: PlayOptions = { loop: false }, + ): Promise { + const playRequest: PlayRequest = { + playSources: playSources.map((source) => this.createPlaySourceInternal(source)), + playTo: playTo.map((identifier) => serializeCommunicationIdentifier(identifier)), + playOptions: { + loop: false, + }, + operationContext: options.operationContext, + operationCallbackUri: options.operationCallbackUrl, + }; - if (options.loop !== undefined) { - playRequest.playOptions = playRequest.playOptions || { loop: false }; // Ensure playOptions is defined - playRequest.playOptions.loop = options.loop; - } - return this.callMedia.play(this.callConnectionId, playRequest, options); + if (options.loop !== undefined) { + playRequest.playOptions = playRequest.playOptions || { loop: false }; // Ensure playOptions is defined + playRequest.playOptions.loop = options.loop; } + return this.callMedia.play(this.callConnectionId, playRequest, options); + } - /** - * Play to all participants. - * - * @param playSources - A PlaySource representing the sources to play. Currently only single play source per request is supported. - * @param options - Additional attributes for play. - */ - public async playToAll( - playSources: (FileSource | TextSource | SsmlSource)[], - options: PlayToAllOptions = { loop: false }, - ): Promise { - const playRequest: PlayRequest = { - playSources: playSources.map((source) => this.createPlaySourceInternal(source)), - playTo: [], - playOptions: { - loop: false, - }, - interruptCallMediaOperation: - options.interruptCallMediaOperation !== undefined - ? options.interruptCallMediaOperation - : false, - operationContext: options.operationContext, - operationCallbackUri: options.operationCallbackUrl, - }; + /** + * Play to all participants. + * + * @param playSources - A PlaySource representing the sources to play. Currently only single play source per request is supported. + * @param options - Additional attributes for play. + */ + public async playToAll( + playSources: (FileSource | TextSource | SsmlSource)[], + options: PlayToAllOptions = { loop: false }, + ): Promise { + const playRequest: PlayRequest = { + playSources: playSources.map((source) => this.createPlaySourceInternal(source)), + playTo: [], + playOptions: { + loop: false, + }, + interruptCallMediaOperation: + options.interruptCallMediaOperation !== undefined + ? options.interruptCallMediaOperation + : false, + operationContext: options.operationContext, + operationCallbackUri: options.operationCallbackUrl, + }; - if (options.loop !== undefined) { - playRequest.playOptions = playRequest.playOptions || { loop: false }; // Ensure playOptions is defined - playRequest.playOptions.loop = options.loop; - } - return this.callMedia.play(this.callConnectionId, playRequest, options); + if (options.loop !== undefined) { + playRequest.playOptions = playRequest.playOptions || { loop: false }; // Ensure playOptions is defined + playRequest.playOptions.loop = options.loop; } + return this.callMedia.play(this.callConnectionId, playRequest, options); + } - private createRecognizeRequest( - targetParticipant: CommunicationIdentifier, - recognizeOptions: - | CallMediaRecognizeDtmfOptions - | CallMediaRecognizeChoiceOptions - | CallMediaRecognizeSpeechOptions - | CallMediaRecognizeSpeechOrDtmfOptions, - ): RecognizeRequest { - if (recognizeOptions.kind === "callMediaRecognizeDtmfOptions") { - const dtmfOptionsInternal: DtmfOptions = { - interToneTimeoutInSeconds: recognizeOptions.interToneTimeoutInSeconds - ? recognizeOptions.interToneTimeoutInSeconds - : 2, - maxTonesToCollect: recognizeOptions.maxTonesToCollect, - stopTones: recognizeOptions.stopDtmfTones, - }; - const recognizeOptionsInternal: RecognizeOptions = { - interruptPrompt: recognizeOptions.interruptPrompt, - initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds - ? recognizeOptions.initialSilenceTimeoutInSeconds - : 5, - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - dtmfOptions: dtmfOptionsInternal, - }; - return { - recognizeInputType: KnownRecognizeInputType.Dtmf, - playPrompt: recognizeOptions.playPrompt - ? this.createPlaySourceInternal(recognizeOptions.playPrompt) - : undefined, - playPrompts: - recognizeOptions.playPrompts !== undefined - ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) - : undefined, - interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, - recognizeOptions: recognizeOptionsInternal, - operationContext: recognizeOptions.operationContext, - operationCallbackUri: recognizeOptions.operationCallbackUrl, - }; - } else if (recognizeOptions.kind === "callMediaRecognizeChoiceOptions") { - const recognizeOptionsInternal: RecognizeOptions = { - interruptPrompt: recognizeOptions.interruptPrompt, - initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds - ? recognizeOptions.initialSilenceTimeoutInSeconds - : 5, - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - speechLanguage: recognizeOptions.speechLanguage, - speechRecognitionModelEndpointId: recognizeOptions.speechRecognitionModelEndpointId, - choices: recognizeOptions.choices, - }; - return { - recognizeInputType: KnownRecognizeInputType.Choices, - playPrompt: recognizeOptions.playPrompt - ? this.createPlaySourceInternal(recognizeOptions.playPrompt) - : undefined, - playPrompts: - recognizeOptions.playPrompts !== undefined - ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) - : undefined, - interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, - recognizeOptions: recognizeOptionsInternal, - operationContext: recognizeOptions.operationContext, - operationCallbackUri: recognizeOptions.operationCallbackUrl, - }; - } else if (recognizeOptions.kind === "callMediaRecognizeSpeechOptions") { - const speechOptions: SpeechOptions = { - endSilenceTimeoutInMs: recognizeOptions.endSilenceTimeoutInSeconds - ? recognizeOptions.endSilenceTimeoutInSeconds * 1000 - : 2000, - }; - const recognizeOptionsInternal: RecognizeOptions = { - interruptPrompt: recognizeOptions.interruptPrompt, - initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds - ? recognizeOptions.initialSilenceTimeoutInSeconds - : 5, - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - speechOptions: speechOptions, - speechLanguage: recognizeOptions.speechLanguage, - speechRecognitionModelEndpointId: recognizeOptions.speechRecognitionModelEndpointId, - }; - return { - recognizeInputType: KnownRecognizeInputType.Speech, - playPrompt: recognizeOptions.playPrompt - ? this.createPlaySourceInternal(recognizeOptions.playPrompt) - : undefined, - playPrompts: - recognizeOptions.playPrompts !== undefined - ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) - : undefined, - interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, - recognizeOptions: recognizeOptionsInternal, - operationContext: recognizeOptions.operationContext, - operationCallbackUri: recognizeOptions.operationCallbackUrl, - }; - } else if (recognizeOptions.kind === "callMediaRecognizeSpeechOrDtmfOptions") { - const dtmfOptionsInternal: DtmfOptions = { - interToneTimeoutInSeconds: recognizeOptions.interToneTimeoutInSeconds - ? recognizeOptions.interToneTimeoutInSeconds - : 2, - maxTonesToCollect: recognizeOptions.maxTonesToCollect, - stopTones: recognizeOptions.stopDtmfTones, - }; - const speechOptions: SpeechOptions = { - endSilenceTimeoutInMs: recognizeOptions.endSilenceTimeoutInSeconds - ? recognizeOptions.endSilenceTimeoutInSeconds * 1000 - : 2000, - }; - const recognizeOptionsInternal: RecognizeOptions = { - interruptPrompt: recognizeOptions.interruptPrompt, - initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds - ? recognizeOptions.initialSilenceTimeoutInSeconds - : 5, - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - speechOptions: speechOptions, - dtmfOptions: dtmfOptionsInternal, - speechRecognitionModelEndpointId: recognizeOptions.speechRecognitionModelEndpointId, - }; - return { - recognizeInputType: KnownRecognizeInputType.SpeechOrDtmf, - playPrompt: recognizeOptions.playPrompt - ? this.createPlaySourceInternal(recognizeOptions.playPrompt) - : undefined, - playPrompts: - recognizeOptions.playPrompts !== undefined - ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) - : undefined, - interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, - recognizeOptions: recognizeOptionsInternal, - operationContext: recognizeOptions.operationContext, - operationCallbackUri: recognizeOptions.operationCallbackUrl, - }; - } - throw new Error("Invalid recognizeOptions"); + private createRecognizeRequest( + targetParticipant: CommunicationIdentifier, + recognizeOptions: + | CallMediaRecognizeDtmfOptions + | CallMediaRecognizeChoiceOptions + | CallMediaRecognizeSpeechOptions + | CallMediaRecognizeSpeechOrDtmfOptions, + ): RecognizeRequest { + if (recognizeOptions.kind === "callMediaRecognizeDtmfOptions") { + const dtmfOptionsInternal: DtmfOptions = { + interToneTimeoutInSeconds: recognizeOptions.interToneTimeoutInSeconds + ? recognizeOptions.interToneTimeoutInSeconds + : 2, + maxTonesToCollect: recognizeOptions.maxTonesToCollect, + stopTones: recognizeOptions.stopDtmfTones, + }; + const recognizeOptionsInternal: RecognizeOptions = { + interruptPrompt: recognizeOptions.interruptPrompt, + initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds + ? recognizeOptions.initialSilenceTimeoutInSeconds + : 5, + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + dtmfOptions: dtmfOptionsInternal, + }; + return { + recognizeInputType: KnownRecognizeInputType.Dtmf, + playPrompt: recognizeOptions.playPrompt + ? this.createPlaySourceInternal(recognizeOptions.playPrompt) + : undefined, + playPrompts: + recognizeOptions.playPrompts !== undefined + ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) + : undefined, + interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, + recognizeOptions: recognizeOptionsInternal, + operationContext: recognizeOptions.operationContext, + operationCallbackUri: recognizeOptions.operationCallbackUrl, + }; + } else if (recognizeOptions.kind === "callMediaRecognizeChoiceOptions") { + const recognizeOptionsInternal: RecognizeOptions = { + interruptPrompt: recognizeOptions.interruptPrompt, + initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds + ? recognizeOptions.initialSilenceTimeoutInSeconds + : 5, + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + speechLanguage: recognizeOptions.speechLanguage, + speechRecognitionModelEndpointId: recognizeOptions.speechRecognitionModelEndpointId, + choices: recognizeOptions.choices, + }; + return { + recognizeInputType: KnownRecognizeInputType.Choices, + playPrompt: recognizeOptions.playPrompt + ? this.createPlaySourceInternal(recognizeOptions.playPrompt) + : undefined, + playPrompts: + recognizeOptions.playPrompts !== undefined + ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) + : undefined, + interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, + recognizeOptions: recognizeOptionsInternal, + operationContext: recognizeOptions.operationContext, + operationCallbackUri: recognizeOptions.operationCallbackUrl, + }; + } else if (recognizeOptions.kind === "callMediaRecognizeSpeechOptions") { + const speechOptions: SpeechOptions = { + endSilenceTimeoutInMs: recognizeOptions.endSilenceTimeoutInSeconds + ? recognizeOptions.endSilenceTimeoutInSeconds * 1000 + : 2000, + }; + const recognizeOptionsInternal: RecognizeOptions = { + interruptPrompt: recognizeOptions.interruptPrompt, + initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds + ? recognizeOptions.initialSilenceTimeoutInSeconds + : 5, + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + speechOptions: speechOptions, + speechLanguage: recognizeOptions.speechLanguage, + speechRecognitionModelEndpointId: recognizeOptions.speechRecognitionModelEndpointId, + }; + return { + recognizeInputType: KnownRecognizeInputType.Speech, + playPrompt: recognizeOptions.playPrompt + ? this.createPlaySourceInternal(recognizeOptions.playPrompt) + : undefined, + playPrompts: + recognizeOptions.playPrompts !== undefined + ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) + : undefined, + interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, + recognizeOptions: recognizeOptionsInternal, + operationContext: recognizeOptions.operationContext, + operationCallbackUri: recognizeOptions.operationCallbackUrl, + }; + } else if (recognizeOptions.kind === "callMediaRecognizeSpeechOrDtmfOptions") { + const dtmfOptionsInternal: DtmfOptions = { + interToneTimeoutInSeconds: recognizeOptions.interToneTimeoutInSeconds + ? recognizeOptions.interToneTimeoutInSeconds + : 2, + maxTonesToCollect: recognizeOptions.maxTonesToCollect, + stopTones: recognizeOptions.stopDtmfTones, + }; + const speechOptions: SpeechOptions = { + endSilenceTimeoutInMs: recognizeOptions.endSilenceTimeoutInSeconds + ? recognizeOptions.endSilenceTimeoutInSeconds * 1000 + : 2000, + }; + const recognizeOptionsInternal: RecognizeOptions = { + interruptPrompt: recognizeOptions.interruptPrompt, + initialSilenceTimeoutInSeconds: recognizeOptions.initialSilenceTimeoutInSeconds + ? recognizeOptions.initialSilenceTimeoutInSeconds + : 5, + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + speechOptions: speechOptions, + dtmfOptions: dtmfOptionsInternal, + speechRecognitionModelEndpointId: recognizeOptions.speechRecognitionModelEndpointId, + }; + return { + recognizeInputType: KnownRecognizeInputType.SpeechOrDtmf, + playPrompt: recognizeOptions.playPrompt + ? this.createPlaySourceInternal(recognizeOptions.playPrompt) + : undefined, + playPrompts: + recognizeOptions.playPrompts !== undefined + ? recognizeOptions.playPrompts.map((source) => this.createPlaySourceInternal(source)) + : undefined, + interruptCallMediaOperation: recognizeOptions.interruptCallMediaOperation, + recognizeOptions: recognizeOptionsInternal, + operationContext: recognizeOptions.operationContext, + operationCallbackUri: recognizeOptions.operationCallbackUrl, + }; } + throw new Error("Invalid recognizeOptions"); + } - /** - * Recognize participant input. - * @deprecated This method signature is deprecated. Please use the new signature with targetParticipant and options params instead, and set maxTonesToCollect in options. - * @param targetParticipant - Target participant. - * @param maxTonesToCollect - Maximum number of DTMF tones to be collected. - * @param options - Different attributes for recognize. - * */ - public async startRecognizing( - targetParticipant: CommunicationIdentifier, - maxTonesToCollect: number, - options: CallMediaRecognizeDtmfOptions, - ): Promise; + /** + * Recognize participant input. + * @deprecated This method signature is deprecated. Please use the new signature with targetParticipant and options params instead, and set maxTonesToCollect in options. + * @param targetParticipant - Target participant. + * @param maxTonesToCollect - Maximum number of DTMF tones to be collected. + * @param options - Different attributes for recognize. + * */ + public async startRecognizing( + targetParticipant: CommunicationIdentifier, + maxTonesToCollect: number, + options: CallMediaRecognizeDtmfOptions, + ): Promise; - /** - * Recognize participant input. - * @param targetParticipant - Target participant. - * @param options - Different attributes for recognize. - * */ - public async startRecognizing( - targetParticipant: CommunicationIdentifier, - options: - | CallMediaRecognizeDtmfOptions - | CallMediaRecognizeChoiceOptions - | CallMediaRecognizeSpeechOptions - | CallMediaRecognizeSpeechOrDtmfOptions, - ): Promise; - async startRecognizing( - targetParticipant: CommunicationIdentifier, - maxTonesOrOptions: - | number - | CallMediaRecognizeDtmfOptions - | CallMediaRecognizeChoiceOptions - | CallMediaRecognizeSpeechOptions - | CallMediaRecognizeSpeechOrDtmfOptions, - options?: CallMediaRecognizeDtmfOptions, - ): Promise { - if (typeof maxTonesOrOptions === "number" && options) { - // Old function signature logic - console.warn( - "Deprecated function signature used. Please use the new signature with targetParticipant and options params instead, and set maxTonesToCollect in options.", - ); - options.maxTonesToCollect = maxTonesOrOptions; - return this.callMedia.recognize( - this.callConnectionId, - this.createRecognizeRequest(targetParticipant, options), - {}, - ); - } else if (typeof maxTonesOrOptions !== "number" && !options) { - // New function signature logic - return this.callMedia.recognize( - this.callConnectionId, - this.createRecognizeRequest(targetParticipant, maxTonesOrOptions), - {}, - ); - } - throw new Error("Invalid params"); + /** + * Recognize participant input. + * @param targetParticipant - Target participant. + * @param options - Different attributes for recognize. + * */ + public async startRecognizing( + targetParticipant: CommunicationIdentifier, + options: + | CallMediaRecognizeDtmfOptions + | CallMediaRecognizeChoiceOptions + | CallMediaRecognizeSpeechOptions + | CallMediaRecognizeSpeechOrDtmfOptions, + ): Promise; + async startRecognizing( + targetParticipant: CommunicationIdentifier, + maxTonesOrOptions: + | number + | CallMediaRecognizeDtmfOptions + | CallMediaRecognizeChoiceOptions + | CallMediaRecognizeSpeechOptions + | CallMediaRecognizeSpeechOrDtmfOptions, + options?: CallMediaRecognizeDtmfOptions, + ): Promise { + if (typeof maxTonesOrOptions === "number" && options) { + // Old function signature logic + console.warn( + "Deprecated function signature used. Please use the new signature with targetParticipant and options params instead, and set maxTonesToCollect in options.", + ); + options.maxTonesToCollect = maxTonesOrOptions; + return this.callMedia.recognize( + this.callConnectionId, + this.createRecognizeRequest(targetParticipant, options), + {}, + ); + } else if (typeof maxTonesOrOptions !== "number" && !options) { + // New function signature logic + return this.callMedia.recognize( + this.callConnectionId, + this.createRecognizeRequest(targetParticipant, maxTonesOrOptions), + {}, + ); } + throw new Error("Invalid params"); + } - /** - * Cancels all the queued media operations. - */ - public async cancelAllOperations(): Promise { - return this.callMedia.cancelAllMediaOperations(this.callConnectionId, {}); - } + /** + * Cancels all the queued media operations. + */ + public async cancelAllOperations(): Promise { + return this.callMedia.cancelAllMediaOperations(this.callConnectionId, {}); + } - /** - * Start continuous Dtmf recognition by subscribing to tones. - * @param targetParticipant - Target participant. - * @param options - Additional attributes for continuous Dtmf recognition. - * */ - public async startContinuousDtmfRecognition( - targetParticipant: CommunicationIdentifier, - options: ContinuousDtmfRecognitionOptions = {}, - ): Promise { - const continuousDtmfRecognitionRequest: ContinuousDtmfRecognitionRequest = { - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - operationContext: options.operationContext, - }; - return this.callMedia.startContinuousDtmfRecognition( - this.callConnectionId, - continuousDtmfRecognitionRequest, - {}, - ); - } + /** + * Start continuous Dtmf recognition by subscribing to tones. + * @param targetParticipant - Target participant. + * @param options - Additional attributes for continuous Dtmf recognition. + * */ + public async startContinuousDtmfRecognition( + targetParticipant: CommunicationIdentifier, + options: ContinuousDtmfRecognitionOptions = {}, + ): Promise { + const continuousDtmfRecognitionRequest: ContinuousDtmfRecognitionRequest = { + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + operationContext: options.operationContext, + }; + return this.callMedia.startContinuousDtmfRecognition( + this.callConnectionId, + continuousDtmfRecognitionRequest, + {}, + ); + } - /** - * Stop continuous Dtmf recognition by unsubscribing to tones. - * @param targetParticipant - Target participant. - * @param options - Additional attributes for continuous Dtmf recognition. - * */ - public async stopContinuousDtmfRecognition( - targetParticipant: CommunicationIdentifier, - options: ContinuousDtmfRecognitionOptions = {}, - ): Promise { - const continuousDtmfRecognitionRequest: ContinuousDtmfRecognitionRequest = { - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - operationContext: options.operationContext, - operationCallbackUri: options.operationCallbackUrl, - }; - return this.callMedia.stopContinuousDtmfRecognition( - this.callConnectionId, - continuousDtmfRecognitionRequest, - {}, - ); - } + /** + * Stop continuous Dtmf recognition by unsubscribing to tones. + * @param targetParticipant - Target participant. + * @param options - Additional attributes for continuous Dtmf recognition. + * */ + public async stopContinuousDtmfRecognition( + targetParticipant: CommunicationIdentifier, + options: ContinuousDtmfRecognitionOptions = {}, + ): Promise { + const continuousDtmfRecognitionRequest: ContinuousDtmfRecognitionRequest = { + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + operationContext: options.operationContext, + operationCallbackUri: options.operationCallbackUrl, + }; + return this.callMedia.stopContinuousDtmfRecognition( + this.callConnectionId, + continuousDtmfRecognitionRequest, + {}, + ); + } - /** - * Send Dtmf tones. - * @param tones - List of tones to be sent to target participant. - * @param targetParticipant - Target participant. - * @param options - Additional attributes for send Dtmf tones. - * */ - public async sendDtmfTones( - tones: Tone[] | DtmfTone[], - targetParticipant: CommunicationIdentifier, - options: SendDtmfTonesOptions = {}, - ): Promise { - const sendDtmfTonesRequest: SendDtmfTonesRequest = { - tones: tones, - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - operationContext: options.operationContext, - operationCallbackUri: options.operationCallbackUrl, - }; + /** + * Send Dtmf tones. + * @param tones - List of tones to be sent to target participant. + * @param targetParticipant - Target participant. + * @param options - Additional attributes for send Dtmf tones. + * */ + public async sendDtmfTones( + tones: Tone[] | DtmfTone[], + targetParticipant: CommunicationIdentifier, + options: SendDtmfTonesOptions = {}, + ): Promise { + const sendDtmfTonesRequest: SendDtmfTonesRequest = { + tones: tones, + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + operationContext: options.operationContext, + operationCallbackUri: options.operationCallbackUrl, + }; - const optionsInternal = { - ...options, - repeatabilityFirstSent: new Date(), - repeatabilityRequestID: randomUUID(), - }; - const result = await this.callMedia.sendDtmfTones( - this.callConnectionId, - sendDtmfTonesRequest, - optionsInternal, - ); - const sendDtmfTonesResult: SendDtmfTonesResult = { - ...result, - }; - return sendDtmfTonesResult; - } + const optionsInternal = { + ...options, + repeatabilityFirstSent: new Date(), + repeatabilityRequestID: randomUUID(), + }; + const result = await this.callMedia.sendDtmfTones( + this.callConnectionId, + sendDtmfTonesRequest, + optionsInternal, + ); + const sendDtmfTonesResult: SendDtmfTonesResult = { + ...result, + }; + return sendDtmfTonesResult; + } - /** - * Put participant on hold while playing audio. - * - * @param targetParticipant - The targets to play to. - * @param options - Additional attributes for hold participant. - */ - public async hold( - targetParticipant: CommunicationIdentifier, - options: HoldOptions = {}, - ): Promise { - const holdRequest: HoldRequest = { - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - playSourceInfo: - options.playSource !== undefined - ? this.createPlaySourceInternal(options.playSource) - : undefined, - operationContext: - options.operationContext !== undefined ? options.operationContext : undefined, - operationCallbackUri: - options.operationCallbackUrl !== undefined ? options.operationCallbackUrl : undefined, - }; - return this.callMedia.hold(this.callConnectionId, holdRequest); - } + /** + * Put participant on hold while playing audio. + * + * @param targetParticipant - The targets to play to. + * @param options - Additional attributes for hold participant. + */ + public async hold( + targetParticipant: CommunicationIdentifier, + options: HoldOptions = {}, + ): Promise { + const holdRequest: HoldRequest = { + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + playSourceInfo: + options.playSource !== undefined + ? this.createPlaySourceInternal(options.playSource) + : undefined, + operationContext: + options.operationContext !== undefined ? options.operationContext : undefined, + operationCallbackUri: + options.operationCallbackUrl !== undefined ? options.operationCallbackUrl : undefined, + }; + return this.callMedia.hold(this.callConnectionId, holdRequest); + } - /** - * Remove participant from hold. - * - * @param targetParticipant - The targets to play to. - * @param options - Additional attributes for unhold participant. - */ - public async unhold( - targetParticipant: CommunicationIdentifier, - options: UnholdOptions = {}, - ): Promise { - const unholdRequest: UnholdRequest = { - targetParticipant: serializeCommunicationIdentifier(targetParticipant), - operationContext: - options.operationContext !== undefined ? options.operationContext : undefined, - }; - return this.callMedia.unhold(this.callConnectionId, unholdRequest); - } + /** + * Remove participant from hold. + * + * @param targetParticipant - The targets to play to. + * @param options - Additional attributes for unhold participant. + */ + public async unhold( + targetParticipant: CommunicationIdentifier, + options: UnholdOptions = {}, + ): Promise { + const unholdRequest: UnholdRequest = { + targetParticipant: serializeCommunicationIdentifier(targetParticipant), + operationContext: + options.operationContext !== undefined ? options.operationContext : undefined, + }; + return this.callMedia.unhold(this.callConnectionId, unholdRequest); + } - /** - * Starts transcription in the call - * @param options - Additional attributes for start transcription. - */ - public async startTranscription(options: StartTranscriptionOptions = {}): Promise { - const startTranscriptionRequest: StartTranscriptionRequest = { - locale: options.locale, - operationContext: options.operationContext, - speechRecognitionModelEndpointId: options.speechRecognitionModelEndpointId, - }; - return this.callMedia.startTranscription(this.callConnectionId, startTranscriptionRequest, {}); - } + /** + * Starts transcription in the call + * @param options - Additional attributes for start transcription. + */ + public async startTranscription(options: StartTranscriptionOptions = {}): Promise { + const startTranscriptionRequest: StartTranscriptionRequest = { + locale: options.locale, + operationContext: options.operationContext, + speechRecognitionModelEndpointId: options.speechRecognitionModelEndpointId, + }; + return this.callMedia.startTranscription(this.callConnectionId, startTranscriptionRequest, {}); + } - /** - * Stops transcription in the call. - * @param options - Additional attributes for stop transcription. - */ - public async stopTranscription(options: StopTranscriptionOptions = {}): Promise { - const stopTranscriptionRequest: StopTranscriptionRequest = { - operationContext: options.operationContext, - }; - return this.callMedia.stopTranscription(this.callConnectionId, stopTranscriptionRequest, {}); - } + /** + * Stops transcription in the call. + * @param options - Additional attributes for stop transcription. + */ + public async stopTranscription(options: StopTranscriptionOptions = {}): Promise { + const stopTranscriptionRequest: StopTranscriptionRequest = { + operationContext: options.operationContext, + }; + return this.callMedia.stopTranscription(this.callConnectionId, stopTranscriptionRequest, {}); + } - /** - * Update transcription language. - * @param locale - Defines new locale for transcription. - */ - public async updateTranscription( - locale: string, - options?: UpdateTranscriptionOptions, - ): Promise { - const updateTranscriptionRequest: UpdateTranscriptionRequest = { - locale: locale, - speechRecognitionModelEndpointId: options?.speechRecognitionModelEndpointId, - operationContext: options?.operationContext, - }; - return this.callMedia.updateTranscription( - this.callConnectionId, - updateTranscriptionRequest, - {}, - ); - } + /** + * Update transcription language. + * @param locale - Defines new locale for transcription. + */ + public async updateTranscription( + locale: string, + options?: UpdateTranscriptionOptions, + ): Promise { + const updateTranscriptionRequest: UpdateTranscriptionRequest = { + locale: locale, + speechRecognitionModelEndpointId: options?.speechRecognitionModelEndpointId, + operationContext: options?.operationContext, + }; + return this.callMedia.updateTranscription( + this.callConnectionId, + updateTranscriptionRequest, + {}, + ); + } - /** - * Starts media streaming in the call. - * @param options - Additional attributes for start media streaming. - */ - public async startMediaStreaming(options: StartMediaStreamingOptions = {}): Promise { - const startMediaStreamingRequest: StartMediaStreamingRequest = { - operationContext: options.operationContext, - operationCallbackUri: options.operationCallbackUrl, - }; - return this.callMedia.startMediaStreaming( - this.callConnectionId, - startMediaStreamingRequest, - options, - ); - } + /** + * Starts media streaming in the call. + * @param options - Additional attributes for start media streaming. + */ + public async startMediaStreaming(options: StartMediaStreamingOptions = {}): Promise { + const startMediaStreamingRequest: StartMediaStreamingRequest = { + operationContext: options.operationContext, + operationCallbackUri: options.operationCallbackUrl, + }; + return this.callMedia.startMediaStreaming( + this.callConnectionId, + startMediaStreamingRequest, + options, + ); + } - /** - * Stops media streaming in the call. - * @param options - Additional attributes for stop media streaming. - */ - public async stopMediaStreaming(options: StopMediaStreamingOptions = {}): Promise { - const stopMediaStreamingRequest: StopMediaStreamingRequest = { - operationCallbackUri: options.operationCallbackUrl, - operationContext: options?.operationContext, - }; - return this.callMedia.stopMediaStreaming( - this.callConnectionId, - stopMediaStreamingRequest, - options, - ); - } + /** + * Stops media streaming in the call. + * @param options - Additional attributes for stop media streaming. + */ + public async stopMediaStreaming(options: StopMediaStreamingOptions = {}): Promise { + const stopMediaStreamingRequest: StopMediaStreamingRequest = { + operationCallbackUri: options.operationCallbackUrl, + operationContext: options?.operationContext, + }; + return this.callMedia.stopMediaStreaming( + this.callConnectionId, + stopMediaStreamingRequest, + options, + ); + } } diff --git a/sdk/communication/communication-call-automation/src/contentDownloader.ts b/sdk/communication/communication-call-automation/src/contentDownloader.ts index 3b9a31471c16..2f417a2f120c 100644 --- a/sdk/communication/communication-call-automation/src/contentDownloader.ts +++ b/sdk/communication/communication-call-automation/src/contentDownloader.ts @@ -3,130 +3,130 @@ import { CallAutomationApiClient } from "./generated/src/callAutomationApiClient"; import { - AddPipelineOptions, - createHttpHeaders, - createPipelineRequest, - PipelineRequest, - PipelineRequestOptions, - PipelineResponse, - SendRequest, + AddPipelineOptions, + createHttpHeaders, + createPipelineRequest, + PipelineRequest, + PipelineRequestOptions, + PipelineResponse, + SendRequest, } from "@azure/core-rest-pipeline"; import { DeleteRecordingOptions, DownloadRecordingOptions } from "./models/options"; /** Class containing ContentDownloading operations. */ export class ContentDownloaderImpl { - private readonly client: CallAutomationApiClient; - - /** - * Initialize a new instance of the class ContentDownloader class. - * @param client - Reference to the service client - */ - constructor(client: CallAutomationApiClient) { - this.client = client; - this.addCustomSignUrlPolicy(); - } - - private addCustomSignUrlPolicy(): void { - const signUrlPolicy = { - name: "CustomSignUrlPolicy", - async sendRequest(request: PipelineRequest, next: SendRequest): Promise { - if (request.headers.has("OriginalUrl")) { - request.url = `${request.headers.get("OriginalUrl")}`; - const originalRequest = new URL(request.url); - request.headers.set("Host", originalRequest.host); - } - return next(request); - }, - }; - - const pipelineOptions: AddPipelineOptions = {}; - pipelineOptions.afterPhase = "Sign"; - this.client.pipeline.addPolicy(signUrlPolicy, pipelineOptions); - } - - /** - * Deletes a recording. - * @param deleteLocationUrl - The recording location url. Required. - */ - async deleteRecording(deleteLocationUrl: string, options: DeleteRecordingOptions): Promise { - const fileLocation = new URL(deleteLocationUrl); - const endpoint = new URL(this.client.endpoint); - const modifiedUrlForSigning = endpoint.origin + fileLocation.pathname; - - const opt: PipelineRequestOptions = { - url: modifiedUrlForSigning, - method: "DELETE", - headers: createHttpHeaders(), - body: "", - abortSignal: options?.abortSignal, - tracingOptions: options?.tracingOptions, - }; - - opt.headers?.set("OriginalUrl", deleteLocationUrl); - opt.headers?.set("x-ms-host", endpoint.host); - opt.headers?.set("accept", "application/json"); - - const req = createPipelineRequest(opt); - - const results = await this.client.sendRequest(req); - - if (results.status !== 200) { - if (results.bodyAsText) { - const jsonBody = JSON.parse(results.bodyAsText); - throw { status: jsonBody.status, message: jsonBody.message }; - } - throw { status: results.status }; + private readonly client: CallAutomationApiClient; + + /** + * Initialize a new instance of the class ContentDownloader class. + * @param client - Reference to the service client + */ + constructor(client: CallAutomationApiClient) { + this.client = client; + this.addCustomSignUrlPolicy(); + } + + private addCustomSignUrlPolicy(): void { + const signUrlPolicy = { + name: "CustomSignUrlPolicy", + async sendRequest(request: PipelineRequest, next: SendRequest): Promise { + if (request.headers.has("OriginalUrl")) { + request.url = `${request.headers.get("OriginalUrl")}`; + const originalRequest = new URL(request.url); + request.headers.set("Host", originalRequest.host); } + return next(request); + }, + }; + + const pipelineOptions: AddPipelineOptions = {}; + pipelineOptions.afterPhase = "Sign"; + this.client.pipeline.addPolicy(signUrlPolicy, pipelineOptions); + } + + /** + * Deletes a recording. + * @param deleteLocationUrl - The recording location url. Required. + */ + async deleteRecording(deleteLocationUrl: string, options: DeleteRecordingOptions): Promise { + const fileLocation = new URL(deleteLocationUrl); + const endpoint = new URL(this.client.endpoint); + const modifiedUrlForSigning = endpoint.origin + fileLocation.pathname; + + const opt: PipelineRequestOptions = { + url: modifiedUrlForSigning, + method: "DELETE", + headers: createHttpHeaders(), + body: "", + abortSignal: options?.abortSignal, + tracingOptions: options?.tracingOptions, + }; + + opt.headers?.set("OriginalUrl", deleteLocationUrl); + opt.headers?.set("x-ms-host", endpoint.host); + opt.headers?.set("accept", "application/json"); + + const req = createPipelineRequest(opt); + + const results = await this.client.sendRequest(req); + + if (results.status !== 200) { + if (results.bodyAsText) { + const jsonBody = JSON.parse(results.bodyAsText); + throw { status: jsonBody.status, message: jsonBody.message }; + } + throw { status: results.status }; + } + } + + /** + * Returns a stream with a call recording. + * @param sourceLocationUrl - The source location url. Required. + * @param options - Additional request options contains downloadRecording options. + */ + async download( + sourceLocationUrl: string, + options: DownloadRecordingOptions, + ): Promise { + const fileLocation = new URL(sourceLocationUrl); + const endpoint = new URL(this.client.endpoint); + const modifiedUrlForSigning = endpoint.origin + fileLocation.pathname; + + const opt: PipelineRequestOptions = { + url: modifiedUrlForSigning, + method: "GET", + headers: createHttpHeaders(), + body: "", + streamResponseStatusCodes: new Set([200, 206]), + abortSignal: options.abortSignal, + tracingOptions: options?.tracingOptions, + }; + + if (options.length && !options.offset) { + throw Error("Download offset value must not be empty if length is set."); + } else if (options.length && options.offset) { + options.length = options.offset + options.length - 1; } - /** - * Returns a stream with a call recording. - * @param sourceLocationUrl - The source location url. Required. - * @param options - Additional request options contains downloadRecording options. - */ - async download( - sourceLocationUrl: string, - options: DownloadRecordingOptions, - ): Promise { - const fileLocation = new URL(sourceLocationUrl); - const endpoint = new URL(this.client.endpoint); - const modifiedUrlForSigning = endpoint.origin + fileLocation.pathname; - - const opt: PipelineRequestOptions = { - url: modifiedUrlForSigning, - method: "GET", - headers: createHttpHeaders(), - body: "", - streamResponseStatusCodes: new Set([200, 206]), - abortSignal: options.abortSignal, - tracingOptions: options?.tracingOptions, - }; - - if (options.length && !options.offset) { - throw Error("Download offset value must not be empty if length is set."); - } else if (options.length && options.offset) { - options.length = options.offset + options.length - 1; - } - - let rangeHeader = "bytes=" + options.offset; - if (options.length) rangeHeader += "-" + options.length; + let rangeHeader = "bytes=" + options.offset; + if (options.length) rangeHeader += "-" + options.length; - opt.headers?.set("OriginalUrl", sourceLocationUrl); - opt.headers?.set("x-ms-host", endpoint.host); - opt.headers?.set("accept", "application/json"); - opt.headers?.set("Range", rangeHeader); + opt.headers?.set("OriginalUrl", sourceLocationUrl); + opt.headers?.set("x-ms-host", endpoint.host); + opt.headers?.set("accept", "application/json"); + opt.headers?.set("Range", rangeHeader); - const req = createPipelineRequest(opt); + const req = createPipelineRequest(opt); - const results = await this.client.sendRequest(req); + const results = await this.client.sendRequest(req); - if (results.status !== 200 && results.status !== 206) { - if (results.bodyAsText) { - const jsonBody = JSON.parse(results.bodyAsText); - throw { status: jsonBody.status, message: jsonBody.message }; - } - throw { status: results.status }; - } - return results; + if (results.status !== 200 && results.status !== 206) { + if (results.bodyAsText) { + const jsonBody = JSON.parse(results.bodyAsText); + throw { status: jsonBody.status, message: jsonBody.message }; + } + throw { status: results.status }; } + return results; + } } diff --git a/sdk/communication/communication-call-automation/src/credential/callAutomationAuthPolicy.ts b/sdk/communication/communication-call-automation/src/credential/callAutomationAuthPolicy.ts index 6e3b325c94f8..03efb7d69b09 100644 --- a/sdk/communication/communication-call-automation/src/credential/callAutomationAuthPolicy.ts +++ b/sdk/communication/communication-call-automation/src/credential/callAutomationAuthPolicy.ts @@ -2,9 +2,9 @@ // Licensed under the MIT License. import { - BearerTokenAuthenticationPolicyOptions, - PipelinePolicy, - bearerTokenAuthenticationPolicy, + BearerTokenAuthenticationPolicyOptions, + PipelinePolicy, + bearerTokenAuthenticationPolicy, } from "@azure/core-rest-pipeline"; import { KeyCredential, TokenCredential, isTokenCredential } from "@azure/core-auth"; import { createCallAutomationAccessKeyCredentialPolicy } from "./callAutomationAccessKeyCredentialPolicy"; @@ -18,18 +18,18 @@ import { createCommunicationAuthPolicy } from "@azure/communication-common"; * @param credential - The KeyCredential or TokenCredential. */ export function createCallAutomationAuthPolicy( - credential: KeyCredential | TokenCredential, - acsUrl: string, + credential: KeyCredential | TokenCredential, + acsUrl: string, ): PipelinePolicy { - if (isTokenCredential(credential)) { - const policyOptions: BearerTokenAuthenticationPolicyOptions = { - credential: credential, - scopes: ["https://communication.azure.com//.default"], - }; - return bearerTokenAuthenticationPolicy(policyOptions); - } else { - return createCallAutomationAccessKeyCredentialPolicy(credential, acsUrl); - } + if (isTokenCredential(credential)) { + const policyOptions: BearerTokenAuthenticationPolicyOptions = { + credential: credential, + scopes: ["https://communication.azure.com//.default"], + }; + return bearerTokenAuthenticationPolicy(policyOptions); + } else { + return createCallAutomationAccessKeyCredentialPolicy(credential, acsUrl); + } } /** @@ -41,24 +41,24 @@ export function createCallAutomationAuthPolicy( * @param url - ACS url. */ export function createCustomCallAutomationApiClient( - credential: KeyCredential | TokenCredential, - internalPipelineOptions: CallAutomationApiClientOptionalParams | undefined, - url: string, + credential: KeyCredential | TokenCredential, + internalPipelineOptions: CallAutomationApiClientOptionalParams | undefined, + url: string, ): CallAutomationApiClient { - // read environment variable for callAutomation auth - const customEnabled = process.env.COMMUNICATION_CUSTOM_ENDPOINT_ENABLED; - const customUrl = process.env.COMMUNICATION_CUSTOM_URL; - let callAutomationApiClient: CallAutomationApiClient; + // read environment variable for callAutomation auth + const customEnabled = process.env.COMMUNICATION_CUSTOM_ENDPOINT_ENABLED; + const customUrl = process.env.COMMUNICATION_CUSTOM_URL; + let callAutomationApiClient: CallAutomationApiClient; - if (customEnabled?.toLowerCase() === "true" && customUrl) { - // add custom header for Call Automation auth when flag is true - callAutomationApiClient = new CallAutomationApiClient(customUrl, internalPipelineOptions); - const callAutomationAuthPolicy = createCallAutomationAuthPolicy(credential, url); - callAutomationApiClient.pipeline.addPolicy(callAutomationAuthPolicy); - } else { - callAutomationApiClient = new CallAutomationApiClient(url, internalPipelineOptions); - const authPolicy = createCommunicationAuthPolicy(credential); - callAutomationApiClient.pipeline.addPolicy(authPolicy); - } - return callAutomationApiClient; + if (customEnabled?.toLowerCase() === "true" && customUrl) { + // add custom header for Call Automation auth when flag is true + callAutomationApiClient = new CallAutomationApiClient(customUrl, internalPipelineOptions); + const callAutomationAuthPolicy = createCallAutomationAuthPolicy(credential, url); + callAutomationApiClient.pipeline.addPolicy(callAutomationAuthPolicy); + } else { + callAutomationApiClient = new CallAutomationApiClient(url, internalPipelineOptions); + const authPolicy = createCommunicationAuthPolicy(credential); + callAutomationApiClient.pipeline.addPolicy(authPolicy); + } + return callAutomationApiClient; } diff --git a/sdk/communication/communication-call-automation/src/models/events.ts b/sdk/communication/communication-call-automation/src/models/events.ts index 936fc589b4e6..0f009be6d267 100644 --- a/sdk/communication/communication-call-automation/src/models/events.ts +++ b/sdk/communication/communication-call-automation/src/models/events.ts @@ -4,729 +4,729 @@ import { CommunicationIdentifier } from "@azure/communication-common"; import { - AddParticipantSucceeded as RestAddParticipantSucceeded, - AddParticipantFailed as RestAddParticipantFailed, - RemoveParticipantSucceeded as RestRemoveParticipantSucceeded, - RemoveParticipantFailed as RestRemoveParticipantFailed, - CallConnected as RestCallConnected, - CallDisconnected as RestCallDisconnected, - CallTransferAccepted as RestCallTransferAccepted, - CallTransferFailed as RestCallTransferFailed, - ParticipantsUpdated as RestParticipantsUpdated, - RecordingStateChanged as RestRecordingStateChanged, - PlayStarted as RestPlayStarted, - PlayCompleted as RestPlayCompleted, - PlayFailed as RestPlayFailed, - PlayCanceled as RestPlayCanceled, - RecognizeCompleted as RestRecognizeCompleted, - RecognizeFailed as RestRecognizeFailed, - RecognizeCanceled as RestRecognizeCanceled, - ResultInformation as RestResultInformation, - ContinuousDtmfRecognitionToneReceived as RestContinuousDtmfRecognitionToneReceived, - ContinuousDtmfRecognitionToneFailed as RestContinuousDtmfRecognitionToneFailed, - ContinuousDtmfRecognitionStopped as RestContinuousDtmfRecognitionStopped, - SendDtmfTonesCompleted as RestSendDtmfTonesCompleted, - SendDtmfTonesFailed as RestSendDtmfTonesFailed, - Tone, - CancelAddParticipantSucceeded as RestCancelAddParticipantSucceeded, - CancelAddParticipantFailed as RestCancelAddParticipantFailed, - ConnectFailed as RestConnectFailed, - TranscriptionStarted as RestTranscriptionStarted, - TranscriptionStopped as RestTranscriptionStopped, - TranscriptionUpdated as RestTranscriptionUpdated, - TranscriptionFailed as RestTranscriptionFailed, - HoldFailed as RestHoldFailed, - MediaStreamingStarted as RestMediaStreamingStarted, - MediaStreamingStopped as RestMediaStreamingStopped, - MediaStreamingFailed as RestMediaStreamingFailed, - CreateCallFailed as RestCreateCallFailed, + AddParticipantSucceeded as RestAddParticipantSucceeded, + AddParticipantFailed as RestAddParticipantFailed, + RemoveParticipantSucceeded as RestRemoveParticipantSucceeded, + RemoveParticipantFailed as RestRemoveParticipantFailed, + CallConnected as RestCallConnected, + CallDisconnected as RestCallDisconnected, + CallTransferAccepted as RestCallTransferAccepted, + CallTransferFailed as RestCallTransferFailed, + ParticipantsUpdated as RestParticipantsUpdated, + RecordingStateChanged as RestRecordingStateChanged, + PlayStarted as RestPlayStarted, + PlayCompleted as RestPlayCompleted, + PlayFailed as RestPlayFailed, + PlayCanceled as RestPlayCanceled, + RecognizeCompleted as RestRecognizeCompleted, + RecognizeFailed as RestRecognizeFailed, + RecognizeCanceled as RestRecognizeCanceled, + ResultInformation as RestResultInformation, + ContinuousDtmfRecognitionToneReceived as RestContinuousDtmfRecognitionToneReceived, + ContinuousDtmfRecognitionToneFailed as RestContinuousDtmfRecognitionToneFailed, + ContinuousDtmfRecognitionStopped as RestContinuousDtmfRecognitionStopped, + SendDtmfTonesCompleted as RestSendDtmfTonesCompleted, + SendDtmfTonesFailed as RestSendDtmfTonesFailed, + Tone, + CancelAddParticipantSucceeded as RestCancelAddParticipantSucceeded, + CancelAddParticipantFailed as RestCancelAddParticipantFailed, + ConnectFailed as RestConnectFailed, + TranscriptionStarted as RestTranscriptionStarted, + TranscriptionStopped as RestTranscriptionStopped, + TranscriptionUpdated as RestTranscriptionUpdated, + TranscriptionFailed as RestTranscriptionFailed, + HoldFailed as RestHoldFailed, + MediaStreamingStarted as RestMediaStreamingStarted, + MediaStreamingStopped as RestMediaStreamingStopped, + MediaStreamingFailed as RestMediaStreamingFailed, + CreateCallFailed as RestCreateCallFailed, } from "../generated/src/models"; import { CallParticipant } from "./models"; /** Callback events for Call Automation */ export type CallAutomationEvent = - | AddParticipantSucceeded - | AddParticipantFailed - | RemoveParticipantSucceeded - | RemoveParticipantFailed - | CallConnected - | CallDisconnected - | CallTransferAccepted - | CallTransferFailed - | ParticipantsUpdated - | RecordingStateChanged - | PlayStarted - | PlayCompleted - | PlayFailed - | PlayCanceled - | RecognizeCompleted - | RecognizeCanceled - | RecognizeFailed - | ContinuousDtmfRecognitionToneReceived - | ContinuousDtmfRecognitionToneFailed - | ContinuousDtmfRecognitionStopped - | SendDtmfTonesCompleted - | SendDtmfTonesFailed - | CancelAddParticipantSucceeded - | CancelAddParticipantFailed - | ConnectFailed - | TranscriptionStarted - | TranscriptionStopped - | TranscriptionUpdated - | TranscriptionFailed - | HoldFailed - | MediaStreamingStarted - | MediaStreamingStopped - | MediaStreamingFailed - | CreateCallFailed; + | AddParticipantSucceeded + | AddParticipantFailed + | RemoveParticipantSucceeded + | RemoveParticipantFailed + | CallConnected + | CallDisconnected + | CallTransferAccepted + | CallTransferFailed + | ParticipantsUpdated + | RecordingStateChanged + | PlayStarted + | PlayCompleted + | PlayFailed + | PlayCanceled + | RecognizeCompleted + | RecognizeCanceled + | RecognizeFailed + | ContinuousDtmfRecognitionToneReceived + | ContinuousDtmfRecognitionToneFailed + | ContinuousDtmfRecognitionStopped + | SendDtmfTonesCompleted + | SendDtmfTonesFailed + | CancelAddParticipantSucceeded + | CancelAddParticipantFailed + | ConnectFailed + | TranscriptionStarted + | TranscriptionStopped + | TranscriptionUpdated + | TranscriptionFailed + | HoldFailed + | MediaStreamingStarted + | MediaStreamingStopped + | MediaStreamingFailed + | CreateCallFailed; export { - RestAddParticipantSucceeded, - RestAddParticipantFailed, - RestRemoveParticipantSucceeded, - RestRemoveParticipantFailed, - RestCallConnected, - RestCallDisconnected, - RestCallTransferAccepted, - RestCallTransferFailed, - RestParticipantsUpdated, - RestRecordingStateChanged, - RestPlayStarted, - RestPlayCompleted, - RestPlayFailed, - RestPlayCanceled, - RestRecognizeCompleted, - RestRecognizeFailed, - RestRecognizeCanceled, - RestResultInformation, - RestContinuousDtmfRecognitionToneReceived, - RestContinuousDtmfRecognitionToneFailed, - RestContinuousDtmfRecognitionStopped, - RestSendDtmfTonesCompleted, - RestSendDtmfTonesFailed, - RestCancelAddParticipantSucceeded, - RestCancelAddParticipantFailed, - RestConnectFailed, - RestTranscriptionStarted, - RestTranscriptionStopped, - RestTranscriptionUpdated, - RestTranscriptionFailed, - RestHoldFailed, - RestMediaStreamingStarted, - RestMediaStreamingStopped, - RestMediaStreamingFailed, - RestCreateCallFailed, + RestAddParticipantSucceeded, + RestAddParticipantFailed, + RestRemoveParticipantSucceeded, + RestRemoveParticipantFailed, + RestCallConnected, + RestCallDisconnected, + RestCallTransferAccepted, + RestCallTransferFailed, + RestParticipantsUpdated, + RestRecordingStateChanged, + RestPlayStarted, + RestPlayCompleted, + RestPlayFailed, + RestPlayCanceled, + RestRecognizeCompleted, + RestRecognizeFailed, + RestRecognizeCanceled, + RestResultInformation, + RestContinuousDtmfRecognitionToneReceived, + RestContinuousDtmfRecognitionToneFailed, + RestContinuousDtmfRecognitionStopped, + RestSendDtmfTonesCompleted, + RestSendDtmfTonesFailed, + RestCancelAddParticipantSucceeded, + RestCancelAddParticipantFailed, + RestConnectFailed, + RestTranscriptionStarted, + RestTranscriptionStopped, + RestTranscriptionUpdated, + RestTranscriptionFailed, + RestHoldFailed, + RestMediaStreamingStarted, + RestMediaStreamingStopped, + RestMediaStreamingFailed, + RestCreateCallFailed, }; export interface ResultInformation - extends Omit { - /** The error code. */ - code: number; - /** The sub code of error. */ - subCode: number; - /** The detailed message of the error. */ - message: string; + extends Omit { + /** The error code. */ + code: number; + /** The sub code of error. */ + subCode: number; + /** The detailed message of the error. */ + message: string; } /** The participant successfully added event. */ export interface AddParticipantSucceeded - extends Omit< - RestAddParticipantSucceeded, - "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** The participant in the call. */ - participant?: CommunicationIdentifier; - /** kind of this event. */ - kind: "AddParticipantSucceeded"; + extends Omit< + RestAddParticipantSucceeded, + "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** The participant in the call. */ + participant?: CommunicationIdentifier; + /** kind of this event. */ + kind: "AddParticipantSucceeded"; } /** The failed to add participant event. */ export interface AddParticipantFailed - extends Omit< - RestAddParticipantFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** The participant in the call. */ - participant?: CommunicationIdentifier; - /** kind of this event. */ - kind: "AddParticipantFailed"; + extends Omit< + RestAddParticipantFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** The participant in the call. */ + participant?: CommunicationIdentifier; + /** kind of this event. */ + kind: "AddParticipantFailed"; } /** The participant successfully removed event. */ export interface RemoveParticipantSucceeded - extends Omit< - RestRemoveParticipantSucceeded, - "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** The participant in the call. */ - participant?: CommunicationIdentifier; - /** kind of this event. */ - kind: "RemoveParticipantSucceeded"; + extends Omit< + RestRemoveParticipantSucceeded, + "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** The participant in the call. */ + participant?: CommunicationIdentifier; + /** kind of this event. */ + kind: "RemoveParticipantSucceeded"; } /** The failed to remove participant event. */ export interface RemoveParticipantFailed - extends Omit< - RestRemoveParticipantFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** The participant in the call. */ - participant?: CommunicationIdentifier; - /** kind of this event. */ - kind: "RemoveParticipantFailed"; + extends Omit< + RestRemoveParticipantFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "participant" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** The participant in the call. */ + participant?: CommunicationIdentifier; + /** kind of this event. */ + kind: "RemoveParticipantFailed"; } /** Event when call was established. */ export interface CallConnected - extends Omit { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "CallConnected"; + extends Omit { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "CallConnected"; } /** Event when all participants left and call was terminated. */ export interface CallDisconnected - extends Omit { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "CallDisconnected"; + extends Omit { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "CallDisconnected"; } /** Event when transfer request was successful. */ export interface CallTransferAccepted - extends Omit< - RestCallTransferAccepted, - | "callConnectionId" - | "serverCallId" - | "correlationId" - | "resultInformation" - | "transferee" - | "transferTarget" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** Participant that was transferred away */ - transferee: CommunicationIdentifier; - /** Target that transferee is transferred to */ - transferTarget: CommunicationIdentifier; - /** kind of this event. */ - kind: "CallTransferAccepted"; + extends Omit< + RestCallTransferAccepted, + | "callConnectionId" + | "serverCallId" + | "correlationId" + | "resultInformation" + | "transferee" + | "transferTarget" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** Participant that was transferred away */ + transferee: CommunicationIdentifier; + /** Target that transferee is transferred to */ + transferTarget: CommunicationIdentifier; + /** kind of this event. */ + kind: "CallTransferAccepted"; } /** Event when transfer request was failed. */ export interface CallTransferFailed - extends Omit< - RestCallTransferFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "CallTransferFailed"; + extends Omit< + RestCallTransferFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "CallTransferFailed"; } /** Event when there was an update to participant(s). */ export interface ParticipantsUpdated - extends Omit< - RestParticipantsUpdated, - "callConnectionId" | "serverCallId" | "correlationId" | "participants" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** The list of participants in the call. */ - participants: CallParticipant[]; - /** kind of this event. */ - kind: "ParticipantsUpdated"; + extends Omit< + RestParticipantsUpdated, + "callConnectionId" | "serverCallId" | "correlationId" | "participants" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** The list of participants in the call. */ + participants: CallParticipant[]; + /** kind of this event. */ + kind: "ParticipantsUpdated"; } /** Event when Recording state has been changed. */ export interface RecordingStateChanged - extends Omit { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "RecordingStateChanged"; + extends Omit { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "RecordingStateChanged"; } /** Event when Media play was successfully started. */ export interface PlayStarted - extends Omit< - RestPlayStarted, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "PlayStarted"; + extends Omit< + RestPlayStarted, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "PlayStarted"; } /** Event when Media play was successfully completed. */ export interface PlayCompleted - extends Omit< - RestPlayCompleted, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "PlayCompleted"; + extends Omit< + RestPlayCompleted, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "PlayCompleted"; } /** Event when Media play was failed. */ export interface PlayFailed - extends Omit< - RestPlayFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "PlayFailed"; + extends Omit< + RestPlayFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "PlayFailed"; } /** Event when Media play was canceled by Cancel operation. */ export interface PlayCanceled - extends Omit { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "PlayCanceled"; + extends Omit { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "PlayCanceled"; } /** Event when Media recognize was successfully completed. */ export interface RecognizeCompleted - extends Omit< - RestRecognizeCompleted, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "RecognizeCompleted"; + extends Omit< + RestRecognizeCompleted, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "RecognizeCompleted"; } /** Event when Media recognize was failed. */ export interface RecognizeFailed - extends Omit< - RestRecognizeFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "RecognizeFailed"; + extends Omit< + RestRecognizeFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "RecognizeFailed"; } /** Event when Media recognize was canceled by Cancel operation. */ export interface RecognizeCanceled - extends Omit { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** kind of this event. */ - kind: "RecognizeCanceled"; + extends Omit { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** kind of this event. */ + kind: "RecognizeCanceled"; } /** Event sent when Dtmf tone received from targeted participant in call. */ export interface ContinuousDtmfRecognitionToneReceived - extends Omit< - RestContinuousDtmfRecognitionToneReceived, - | "sequenceId" - | "tone" - | "callConnectionId" - | "serverCallId" - | "correlationId" - | "resultInformation" - > { - /** The sequence id which can be used to determine if the same tone was played multiple times or if any tones were missed. */ - sequenceId: number; - /** Defines values for Tone. */ - tone: Tone; - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId or skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "ContinuousDtmfRecognitionToneReceived"; + extends Omit< + RestContinuousDtmfRecognitionToneReceived, + | "sequenceId" + | "tone" + | "callConnectionId" + | "serverCallId" + | "correlationId" + | "resultInformation" + > { + /** The sequence id which can be used to determine if the same tone was played multiple times or if any tones were missed. */ + sequenceId: number; + /** Defines values for Tone. */ + tone: Tone; + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId or skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "ContinuousDtmfRecognitionToneReceived"; } /** Event sent when failed to recognize continuous Dtmf tone. */ export interface ContinuousDtmfRecognitionToneFailed - extends Omit< - RestContinuousDtmfRecognitionToneFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "ContinuousDtmfRecognitionToneFailed"; + extends Omit< + RestContinuousDtmfRecognitionToneFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "ContinuousDtmfRecognitionToneFailed"; } /** Event sent when continuous Dtmf recognition stopped. */ export interface ContinuousDtmfRecognitionStopped - extends Omit< - RestContinuousDtmfRecognitionStopped, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "ContinuousDtmfRecognitionStopped"; + extends Omit< + RestContinuousDtmfRecognitionStopped, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "ContinuousDtmfRecognitionStopped"; } /** Event sent when Dtmf tones send successfully. */ export interface SendDtmfTonesCompleted - extends Omit< - RestSendDtmfTonesCompleted, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "SendDtmfTonesCompleted"; + extends Omit< + RestSendDtmfTonesCompleted, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "SendDtmfTonesCompleted"; } /** Event sent when Dtmf tones send failed. */ export interface SendDtmfTonesFailed - extends Omit< - RestSendDtmfTonesFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "SendDtmfTonesFailed"; + extends Omit< + RestSendDtmfTonesFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "SendDtmfTonesFailed"; } /** Successful cancel add participant event. */ export interface CancelAddParticipantSucceeded - extends Omit< - RestCancelAddParticipantSucceeded, - "callConnectionId" | "serverCallId" | "correlationId" | "invitationId" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Invitation ID used to cancel the add participant request. */ - invitationId: string; - /** kind of this event. */ - kind: "CancelAddParticipantSucceeded"; + extends Omit< + RestCancelAddParticipantSucceeded, + "callConnectionId" | "serverCallId" | "correlationId" | "invitationId" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Invitation ID used to cancel the add participant request. */ + invitationId: string; + /** kind of this event. */ + kind: "CancelAddParticipantSucceeded"; } /** The failed to cancel add participant event. */ export interface CancelAddParticipantFailed - extends Omit< - RestCancelAddParticipantFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "invitationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Invitation ID used to cancel the add participant request. */ - invitationId: string; - /** Contains the resulting SIP code/sub-code and message from NGC services. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "CancelAddParticipantFailed"; + extends Omit< + RestCancelAddParticipantFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "invitationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Invitation ID used to cancel the add participant request. */ + invitationId: string; + /** Contains the resulting SIP code/sub-code and message from NGC services. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "CancelAddParticipantFailed"; } export interface ConnectFailed - extends Omit< - RestConnectFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "ConnectFailed"; + extends Omit< + RestConnectFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "ConnectFailed"; } export interface TranscriptionStarted - extends Omit< - RestTranscriptionStarted, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: RestResultInformation; - /** kind of this event. */ - kind: "TranscriptionStarted"; + extends Omit< + RestTranscriptionStarted, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: RestResultInformation; + /** kind of this event. */ + kind: "TranscriptionStarted"; } export interface TranscriptionStopped - extends Omit< - RestTranscriptionStopped, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: RestResultInformation; - /** kind of this event. */ - kind: "TranscriptionStopped"; + extends Omit< + RestTranscriptionStopped, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: RestResultInformation; + /** kind of this event. */ + kind: "TranscriptionStopped"; } export interface TranscriptionUpdated - extends Omit< - RestTranscriptionUpdated, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: RestResultInformation; - /** kind of this event. */ - kind: "TranscriptionUpdated"; + extends Omit< + RestTranscriptionUpdated, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: RestResultInformation; + /** kind of this event. */ + kind: "TranscriptionUpdated"; } export interface TranscriptionFailed - extends Omit< - RestTranscriptionFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: RestResultInformation; - /** kind of this event. */ - kind: "TranscriptionFailed"; + extends Omit< + RestTranscriptionFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: RestResultInformation; + /** kind of this event. */ + kind: "TranscriptionFailed"; } export interface HoldFailed - extends Omit< - RestHoldFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "HoldFailed"; + extends Omit< + RestHoldFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "HoldFailed"; } export interface MediaStreamingStarted - extends Omit< - RestMediaStreamingStarted, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "MediaStreamingStarted"; + extends Omit< + RestMediaStreamingStarted, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "MediaStreamingStarted"; } export interface MediaStreamingStopped - extends Omit< - RestMediaStreamingStopped, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "MediaStreamingStopped"; + extends Omit< + RestMediaStreamingStopped, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "MediaStreamingStopped"; } export interface MediaStreamingFailed - extends Omit< - RestMediaStreamingFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "MediaStreamingFailed"; + extends Omit< + RestMediaStreamingFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "MediaStreamingFailed"; } export interface CreateCallFailed - extends Omit< - RestCreateCallFailed, - "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" - > { - /** Call connection ID. */ - callConnectionId: string; - /** Server call ID. */ - serverCallId: string; - /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ - correlationId: string; - /** Contains the resulting SIP code, sub-code and message. */ - resultInformation?: ResultInformation; - /** kind of this event. */ - kind: "CreateCallFailed"; + extends Omit< + RestCreateCallFailed, + "callConnectionId" | "serverCallId" | "correlationId" | "resultInformation" + > { + /** Call connection ID. */ + callConnectionId: string; + /** Server call ID. */ + serverCallId: string; + /** Correlation ID for event to call correlation. Also called ChainId for skype chain ID. */ + correlationId: string; + /** Contains the resulting SIP code, sub-code and message. */ + resultInformation?: ResultInformation; + /** kind of this event. */ + kind: "CreateCallFailed"; } diff --git a/sdk/communication/communication-call-automation/src/utli/converters.ts b/sdk/communication/communication-call-automation/src/utli/converters.ts index f407091dd7fb..b25f5d168f5c 100644 --- a/sdk/communication/communication-call-automation/src/utli/converters.ts +++ b/sdk/communication/communication-call-automation/src/utli/converters.ts @@ -2,220 +2,220 @@ // Licensed under the MIT License. import { - PhoneNumberIdentifier, - CommunicationUserIdentifier, - UnknownIdentifier, - serializeCommunicationIdentifier, - SerializedPhoneNumberIdentifier, - CommunicationIdentifier, - isCommunicationUserIdentifier, - isPhoneNumberIdentifier, - isUnknownIdentifier, - SerializedCommunicationIdentifier, - isMicrosoftTeamsUserIdentifier, - MicrosoftTeamsUserIdentifier, - isMicrosoftTeamsAppIdentifier, - MicrosoftTeamsAppIdentifier, + PhoneNumberIdentifier, + CommunicationUserIdentifier, + UnknownIdentifier, + serializeCommunicationIdentifier, + SerializedPhoneNumberIdentifier, + CommunicationIdentifier, + isCommunicationUserIdentifier, + isPhoneNumberIdentifier, + isUnknownIdentifier, + SerializedCommunicationIdentifier, + isMicrosoftTeamsUserIdentifier, + MicrosoftTeamsUserIdentifier, + isMicrosoftTeamsAppIdentifier, + MicrosoftTeamsAppIdentifier, } from "@azure/communication-common"; import { - CallParticipantInternal, - CommunicationIdentifierModel, - CommunicationIdentifierModelKind, - KnownCommunicationCloudEnvironmentModel, - KnownCommunicationIdentifierModelKind, - PhoneNumberIdentifierModel, - CommunicationUserIdentifierModel, + CallParticipantInternal, + CommunicationIdentifierModel, + CommunicationIdentifierModelKind, + KnownCommunicationCloudEnvironmentModel, + KnownCommunicationIdentifierModelKind, + PhoneNumberIdentifierModel, + CommunicationUserIdentifierModel, } from "../generated/src"; import { CallParticipant } from "../models/models"; function extractKind( - identifierModel: CommunicationIdentifierModel, + identifierModel: CommunicationIdentifierModel, ): CommunicationIdentifierModelKind { - if (identifierModel.communicationUser !== undefined) { - return KnownCommunicationIdentifierModelKind.CommunicationUser; - } - if (identifierModel.phoneNumber !== undefined) { - return KnownCommunicationIdentifierModelKind.PhoneNumber; - } - if (identifierModel.microsoftTeamsUser !== undefined) { - return KnownCommunicationIdentifierModelKind.MicrosoftTeamsUser; - } - if (identifierModel.microsoftTeamsApp !== undefined) { - return KnownCommunicationIdentifierModelKind.MicrosoftTeamsApp; - } - return KnownCommunicationIdentifierModelKind.Unknown; + if (identifierModel.communicationUser !== undefined) { + return KnownCommunicationIdentifierModelKind.CommunicationUser; + } + if (identifierModel.phoneNumber !== undefined) { + return KnownCommunicationIdentifierModelKind.PhoneNumber; + } + if (identifierModel.microsoftTeamsUser !== undefined) { + return KnownCommunicationIdentifierModelKind.MicrosoftTeamsUser; + } + if (identifierModel.microsoftTeamsApp !== undefined) { + return KnownCommunicationIdentifierModelKind.MicrosoftTeamsApp; + } + return KnownCommunicationIdentifierModelKind.Unknown; } /** Convert PhoneNumberIdentifier to PhoneNumberIdentifierModel(Internal usage class) */ export function PhoneNumberIdentifierModelConverter( - phoneNumberIdentifier: PhoneNumberIdentifier | undefined, + phoneNumberIdentifier: PhoneNumberIdentifier | undefined, ): PhoneNumberIdentifierModel | undefined { - if (phoneNumberIdentifier === undefined || phoneNumberIdentifier.phoneNumber === undefined) { - return undefined; - } + if (phoneNumberIdentifier === undefined || phoneNumberIdentifier.phoneNumber === undefined) { + return undefined; + } - const phoneNumberIdentifierModel = - serializeCommunicationIdentifier(phoneNumberIdentifier).phoneNumber; - return phoneNumberIdentifierModel; + const phoneNumberIdentifierModel = + serializeCommunicationIdentifier(phoneNumberIdentifier).phoneNumber; + return phoneNumberIdentifierModel; } /** Convert SerializedPhoneNumberIdentifier to PhoneNumberIdentifier(Public usage class) */ export function phoneNumberIdentifierConverter( - serializedPhoneNumberIdentifier: SerializedPhoneNumberIdentifier | undefined, + serializedPhoneNumberIdentifier: SerializedPhoneNumberIdentifier | undefined, ): PhoneNumberIdentifier | undefined { - if ( - serializedPhoneNumberIdentifier === undefined || - serializedPhoneNumberIdentifier?.value === null - ) { - return undefined; - } - - const phoneNumberIdentifier: PhoneNumberIdentifier = { - phoneNumber: serializedPhoneNumberIdentifier.value, - }; - return phoneNumberIdentifier; + if ( + serializedPhoneNumberIdentifier === undefined || + serializedPhoneNumberIdentifier?.value === null + ) { + return undefined; + } + + const phoneNumberIdentifier: PhoneNumberIdentifier = { + phoneNumber: serializedPhoneNumberIdentifier.value, + }; + return phoneNumberIdentifier; } /** Convert CommunicationIdentifierModel to CommunicationIdentifier(Public usage class) */ export function communicationIdentifierConverter( - identifierModel: CommunicationIdentifierModel, + identifierModel: CommunicationIdentifierModel, ): CommunicationIdentifier { - const rawId = identifierModel.rawId; - const kind = - identifierModel.kind !== undefined ? identifierModel.kind : extractKind(identifierModel); - - if ( - kind === KnownCommunicationIdentifierModelKind.CommunicationUser && - identifierModel.communicationUser !== undefined - ) { - const communicationUserIdentifier: CommunicationUserIdentifier = { - communicationUserId: identifierModel.communicationUser.id, - }; - return communicationUserIdentifier; - } - - if ( - kind === KnownCommunicationIdentifierModelKind.PhoneNumber && - identifierModel.phoneNumber !== undefined - ) { - const phoneNumberIdentifier: PhoneNumberIdentifier = { - phoneNumber: identifierModel.phoneNumber.value, - rawId: rawId, - }; - return phoneNumberIdentifier; - } - - if ( - kind === KnownCommunicationIdentifierModelKind.MicrosoftTeamsUser && - identifierModel.microsoftTeamsUser !== undefined - ) { - const microsoftTeamsUserIdentifier: MicrosoftTeamsUserIdentifier = { - rawId: rawId, - microsoftTeamsUserId: identifierModel.microsoftTeamsUser.userId, - isAnonymous: identifierModel.microsoftTeamsUser.isAnonymous, - cloud: identifierModel.microsoftTeamsUser.cloud as KnownCommunicationCloudEnvironmentModel, - }; - return microsoftTeamsUserIdentifier; - } - - if ( - kind === KnownCommunicationIdentifierModelKind.MicrosoftTeamsApp && - identifierModel.microsoftTeamsApp !== undefined - ) { - const microsoftTeamsAppIdentifier: MicrosoftTeamsAppIdentifier = { - rawId: rawId, - teamsAppId: identifierModel.microsoftTeamsApp.appId, - cloud: identifierModel.microsoftTeamsApp.cloud as KnownCommunicationCloudEnvironmentModel, - }; - return microsoftTeamsAppIdentifier; - } - - const unknownIdentifier: UnknownIdentifier = { - id: rawId ? rawId : "", + const rawId = identifierModel.rawId; + const kind = + identifierModel.kind !== undefined ? identifierModel.kind : extractKind(identifierModel); + + if ( + kind === KnownCommunicationIdentifierModelKind.CommunicationUser && + identifierModel.communicationUser !== undefined + ) { + const communicationUserIdentifier: CommunicationUserIdentifier = { + communicationUserId: identifierModel.communicationUser.id, + }; + return communicationUserIdentifier; + } + + if ( + kind === KnownCommunicationIdentifierModelKind.PhoneNumber && + identifierModel.phoneNumber !== undefined + ) { + const phoneNumberIdentifier: PhoneNumberIdentifier = { + phoneNumber: identifierModel.phoneNumber.value, + rawId: rawId, + }; + return phoneNumberIdentifier; + } + + if ( + kind === KnownCommunicationIdentifierModelKind.MicrosoftTeamsUser && + identifierModel.microsoftTeamsUser !== undefined + ) { + const microsoftTeamsUserIdentifier: MicrosoftTeamsUserIdentifier = { + rawId: rawId, + microsoftTeamsUserId: identifierModel.microsoftTeamsUser.userId, + isAnonymous: identifierModel.microsoftTeamsUser.isAnonymous, + cloud: identifierModel.microsoftTeamsUser.cloud as KnownCommunicationCloudEnvironmentModel, + }; + return microsoftTeamsUserIdentifier; + } + + if ( + kind === KnownCommunicationIdentifierModelKind.MicrosoftTeamsApp && + identifierModel.microsoftTeamsApp !== undefined + ) { + const microsoftTeamsAppIdentifier: MicrosoftTeamsAppIdentifier = { + rawId: rawId, + teamsAppId: identifierModel.microsoftTeamsApp.appId, + cloud: identifierModel.microsoftTeamsApp.cloud as KnownCommunicationCloudEnvironmentModel, }; - return unknownIdentifier; + return microsoftTeamsAppIdentifier; + } + + const unknownIdentifier: UnknownIdentifier = { + id: rawId ? rawId : "", + }; + return unknownIdentifier; } /** Convert CommunicationIdentifier to CommunicationIdentifierModel(Internal usage class) */ export function communicationIdentifierModelConverter( - identifier: CommunicationIdentifier, + identifier: CommunicationIdentifier, ): CommunicationIdentifierModel { - const serializedIdentifier: SerializedCommunicationIdentifier = - serializeCommunicationIdentifier(identifier); - if (isCommunicationUserIdentifier(identifier)) { - const communicationUserIdentifierModel: CommunicationIdentifierModel = { - kind: KnownCommunicationIdentifierModelKind.CommunicationUser, - ...serializedIdentifier, - }; - return communicationUserIdentifierModel; - } - - if (isPhoneNumberIdentifier(identifier)) { - const phoneNumberIdentifierModel: CommunicationIdentifierModel = { - kind: KnownCommunicationIdentifierModelKind.PhoneNumber, - ...serializedIdentifier, - }; - return phoneNumberIdentifierModel; - } - - if (isMicrosoftTeamsUserIdentifier(identifier)) { - const microsoftTeamsUserIdentifierModel: CommunicationIdentifierModel = { - kind: KnownCommunicationIdentifierModelKind.MicrosoftTeamsUser, - ...serializedIdentifier, - }; - return microsoftTeamsUserIdentifierModel; - } - - if (isMicrosoftTeamsAppIdentifier(identifier)) { - const microsoftTeamsAppIdentifierModel: CommunicationIdentifierModel = { - kind: KnownCommunicationIdentifierModelKind.MicrosoftTeamsApp, - ...serializedIdentifier, - }; - return microsoftTeamsAppIdentifierModel; - } - - if (isUnknownIdentifier(identifier)) { - const unknownIdentifierModel: CommunicationIdentifierModel = { - kind: KnownCommunicationIdentifierModelKind.Unknown, - ...serializedIdentifier, - }; - return unknownIdentifierModel; - } - - throw new Error(); + const serializedIdentifier: SerializedCommunicationIdentifier = + serializeCommunicationIdentifier(identifier); + if (isCommunicationUserIdentifier(identifier)) { + const communicationUserIdentifierModel: CommunicationIdentifierModel = { + kind: KnownCommunicationIdentifierModelKind.CommunicationUser, + ...serializedIdentifier, + }; + return communicationUserIdentifierModel; + } + + if (isPhoneNumberIdentifier(identifier)) { + const phoneNumberIdentifierModel: CommunicationIdentifierModel = { + kind: KnownCommunicationIdentifierModelKind.PhoneNumber, + ...serializedIdentifier, + }; + return phoneNumberIdentifierModel; + } + + if (isMicrosoftTeamsUserIdentifier(identifier)) { + const microsoftTeamsUserIdentifierModel: CommunicationIdentifierModel = { + kind: KnownCommunicationIdentifierModelKind.MicrosoftTeamsUser, + ...serializedIdentifier, + }; + return microsoftTeamsUserIdentifierModel; + } + + if (isMicrosoftTeamsAppIdentifier(identifier)) { + const microsoftTeamsAppIdentifierModel: CommunicationIdentifierModel = { + kind: KnownCommunicationIdentifierModelKind.MicrosoftTeamsApp, + ...serializedIdentifier, + }; + return microsoftTeamsAppIdentifierModel; + } + + if (isUnknownIdentifier(identifier)) { + const unknownIdentifierModel: CommunicationIdentifierModel = { + kind: KnownCommunicationIdentifierModelKind.Unknown, + ...serializedIdentifier, + }; + return unknownIdentifierModel; + } + + throw new Error(); } /** Convert CallParticipantInternal to CallParticipant */ export function callParticipantConverter( - acsCallParticipant: CallParticipantInternal, + acsCallParticipant: CallParticipantInternal, ): CallParticipant { - const callParticipant: CallParticipant = { - ...acsCallParticipant, - identifier: acsCallParticipant.identifier - ? communicationIdentifierConverter(acsCallParticipant.identifier) - : undefined, - }; - return callParticipant; + const callParticipant: CallParticipant = { + ...acsCallParticipant, + identifier: acsCallParticipant.identifier + ? communicationIdentifierConverter(acsCallParticipant.identifier) + : undefined, + }; + return callParticipant; } /** Convert CommunicationUserIdentifier to CommunicationUserIdentifierModel (Internal usage class) */ export function communicationUserIdentifierModelConverter( - identifier: CommunicationUserIdentifier | undefined, + identifier: CommunicationUserIdentifier | undefined, ): CommunicationUserIdentifierModel | undefined { - if (!identifier || !identifier.communicationUserId) { - return undefined; - } + if (!identifier || !identifier.communicationUserId) { + return undefined; + } - return { id: identifier.communicationUserId }; + return { id: identifier.communicationUserId }; } /** Convert CommunicationUserIdentifierModel to CommunicationUserIdentifier (Public usage class) */ export function communicationUserIdentifierConverter( - identifier: CommunicationUserIdentifierModel | undefined, + identifier: CommunicationUserIdentifierModel | undefined, ): CommunicationUserIdentifier | undefined { - if (!identifier || !identifier.id) { - return undefined; - } + if (!identifier || !identifier.id) { + return undefined; + } - return { communicationUserId: identifier.id }; + return { communicationUserId: identifier.id }; } diff --git a/sdk/communication/communication-call-automation/test/callAutomationClient.spec.ts b/sdk/communication/communication-call-automation/test/callAutomationClient.spec.ts index bc01cc218f12..9b263125781a 100644 --- a/sdk/communication/communication-call-automation/test/callAutomationClient.spec.ts +++ b/sdk/communication/communication-call-automation/test/callAutomationClient.spec.ts @@ -6,293 +6,293 @@ import Sinon, { SinonStubbedInstance } from "sinon"; import { CallConnectionProperties } from "../src/models/models"; import { AnswerCallResult, CreateCallResult } from "../src/models/responses"; import { - CALL_CALLBACK_URL, - CALL_INCOMING_CALL_CONTEXT, - CALL_TARGET_ID, - CALL_TARGET_ID_2, + CALL_CALLBACK_URL, + CALL_INCOMING_CALL_CONTEXT, + CALL_TARGET_ID, + CALL_TARGET_ID_2, } from "./utils/connectionUtils"; import { CommunicationIdentifier, CommunicationUserIdentifier } from "@azure/communication-common"; import { assert } from "chai"; import { Context } from "mocha"; import { - CallAutomationClient, - CallInvite, - CallConnection, - CreateCallOptions, - AnswerCallOptions, + CallAutomationClient, + CallInvite, + CallConnection, + CreateCallOptions, + AnswerCallOptions, } from "../src"; import { - createRecorder, - createTestUser, - dispatcherCallback, - serviceBusWithNewCall, - createCallAutomationClient, - waitForIncomingCallContext, - waitForEvent, - events, - serviceBusReceivers, - incomingCallContexts, - loadPersistedEvents, - persistEvents, + createRecorder, + createTestUser, + dispatcherCallback, + serviceBusWithNewCall, + createCallAutomationClient, + waitForIncomingCallContext, + waitForEvent, + events, + serviceBusReceivers, + incomingCallContexts, + loadPersistedEvents, + persistEvents, } from "./utils/recordedClient"; import { randomUUID } from "@azure/core-util"; describe("Call Automation Client Unit Tests", () => { - let targets: CommunicationIdentifier[]; - let target: CallInvite; - let client: SinonStubbedInstance & CallAutomationClient; - - beforeEach(() => { - // set up - targets = [ - { - communicationUserId: CALL_TARGET_ID, - }, - { - communicationUserId: CALL_TARGET_ID_2, - }, - ]; - target = { - targetParticipant: { communicationUserId: CALL_TARGET_ID }, - }; - // stub CallAutomationClient - client = Sinon.createStubInstance( - CallAutomationClient, - ) as SinonStubbedInstance & CallAutomationClient; - }); - - it("RepeatabilityHeadersGeneration", async () => { - // mocks - const repeatabilityFirstSent: string = new Date().toUTCString(); - const repeatabilityRequestID: string = randomUUID(); - - // asserts - assert.isNotNull(repeatabilityFirstSent); - assert.isNotNull(repeatabilityRequestID); - assert.typeOf(repeatabilityFirstSent, "string"); - assert.typeOf(repeatabilityRequestID, "string"); - }); - - it("CreateCall", async () => { - // mocks - const createCallResultMock: CreateCallResult = { - callConnectionProperties: {} as CallConnectionProperties, - callConnection: {} as CallConnection, - }; - client.createCall.returns( - new Promise((resolve) => { - resolve(createCallResultMock); - }), - ); - - const promiseResult = client.createCall(target, CALL_CALLBACK_URL); - - // asserts - promiseResult - .then((result: CreateCallResult) => { - assert.isNotNull(result); - assert.isTrue(client.createCall.calledWith(target, CALL_CALLBACK_URL)); - assert.equal(result, createCallResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("CreateGroupCall", async () => { - // mocks - const createGroupCallResultMock: CreateCallResult = { - callConnectionProperties: {} as CallConnectionProperties, - callConnection: {} as CallConnection, - }; - client.createGroupCall.returns( - new Promise((resolve) => { - resolve(createGroupCallResultMock); - }), - ); - - const promiseResult = client.createGroupCall(targets, CALL_CALLBACK_URL); - - // asserts - promiseResult - .then((result: CreateCallResult) => { - assert.isNotNull(result); - assert.isTrue(client.createGroupCall.calledWith(targets, CALL_CALLBACK_URL)); - assert.equal(result, createGroupCallResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("AnswerCall", async () => { - // mocks - const answerCallResultMock: AnswerCallResult = { - callConnectionProperties: {} as CallConnectionProperties, - callConnection: {} as CallConnection, - }; - client.answerCall.returns( - new Promise((resolve) => { - resolve(answerCallResultMock); - }), - ); - - const promiseResult = client.answerCall(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL); - - // asserts - promiseResult - .then((result: AnswerCallResult) => { - assert.isNotNull(result); - assert.isTrue(client.answerCall.calledWith(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL)); - assert.equal(result, answerCallResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("RedirectCall", async () => { - // mocks - client.redirectCall.returns( - new Promise((resolve) => { - resolve(undefined); - }), - ); - - const promiseResult = client.redirectCall(CALL_INCOMING_CALL_CONTEXT, target); - - // asserts - promiseResult - .then(() => { - assert.isTrue(client.redirectCall.calledWith(CALL_INCOMING_CALL_CONTEXT, target)); - return; - }) - .catch((error) => console.error(error)); - }); - - it("RejectCall", async () => { - // mocks - client.rejectCall.returns( - new Promise((resolve) => { - resolve(undefined); - }), - ); - - const promiseResult = client.rejectCall(CALL_INCOMING_CALL_CONTEXT); - - // asserts - promiseResult - .then(() => { - assert.isTrue(client.rejectCall.calledWith(CALL_INCOMING_CALL_CONTEXT)); - return; - }) - .catch((error) => console.error(error)); - }); + let targets: CommunicationIdentifier[]; + let target: CallInvite; + let client: SinonStubbedInstance & CallAutomationClient; + + beforeEach(() => { + // set up + targets = [ + { + communicationUserId: CALL_TARGET_ID, + }, + { + communicationUserId: CALL_TARGET_ID_2, + }, + ]; + target = { + targetParticipant: { communicationUserId: CALL_TARGET_ID }, + }; + // stub CallAutomationClient + client = Sinon.createStubInstance( + CallAutomationClient, + ) as SinonStubbedInstance & CallAutomationClient; + }); + + it("RepeatabilityHeadersGeneration", async () => { + // mocks + const repeatabilityFirstSent: string = new Date().toUTCString(); + const repeatabilityRequestID: string = randomUUID(); + + // asserts + assert.isNotNull(repeatabilityFirstSent); + assert.isNotNull(repeatabilityRequestID); + assert.typeOf(repeatabilityFirstSent, "string"); + assert.typeOf(repeatabilityRequestID, "string"); + }); + + it("CreateCall", async () => { + // mocks + const createCallResultMock: CreateCallResult = { + callConnectionProperties: {} as CallConnectionProperties, + callConnection: {} as CallConnection, + }; + client.createCall.returns( + new Promise((resolve) => { + resolve(createCallResultMock); + }), + ); + + const promiseResult = client.createCall(target, CALL_CALLBACK_URL); + + // asserts + promiseResult + .then((result: CreateCallResult) => { + assert.isNotNull(result); + assert.isTrue(client.createCall.calledWith(target, CALL_CALLBACK_URL)); + assert.equal(result, createCallResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("CreateGroupCall", async () => { + // mocks + const createGroupCallResultMock: CreateCallResult = { + callConnectionProperties: {} as CallConnectionProperties, + callConnection: {} as CallConnection, + }; + client.createGroupCall.returns( + new Promise((resolve) => { + resolve(createGroupCallResultMock); + }), + ); + + const promiseResult = client.createGroupCall(targets, CALL_CALLBACK_URL); + + // asserts + promiseResult + .then((result: CreateCallResult) => { + assert.isNotNull(result); + assert.isTrue(client.createGroupCall.calledWith(targets, CALL_CALLBACK_URL)); + assert.equal(result, createGroupCallResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("AnswerCall", async () => { + // mocks + const answerCallResultMock: AnswerCallResult = { + callConnectionProperties: {} as CallConnectionProperties, + callConnection: {} as CallConnection, + }; + client.answerCall.returns( + new Promise((resolve) => { + resolve(answerCallResultMock); + }), + ); + + const promiseResult = client.answerCall(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL); + + // asserts + promiseResult + .then((result: AnswerCallResult) => { + assert.isNotNull(result); + assert.isTrue(client.answerCall.calledWith(CALL_INCOMING_CALL_CONTEXT, CALL_CALLBACK_URL)); + assert.equal(result, answerCallResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("RedirectCall", async () => { + // mocks + client.redirectCall.returns( + new Promise((resolve) => { + resolve(undefined); + }), + ); + + const promiseResult = client.redirectCall(CALL_INCOMING_CALL_CONTEXT, target); + + // asserts + promiseResult + .then(() => { + assert.isTrue(client.redirectCall.calledWith(CALL_INCOMING_CALL_CONTEXT, target)); + return; + }) + .catch((error) => console.error(error)); + }); + + it("RejectCall", async () => { + // mocks + client.rejectCall.returns( + new Promise((resolve) => { + resolve(undefined); + }), + ); + + const promiseResult = client.rejectCall(CALL_INCOMING_CALL_CONTEXT); + + // asserts + promiseResult + .then(() => { + assert.isTrue(client.rejectCall.calledWith(CALL_INCOMING_CALL_CONTEXT)); + return; + }) + .catch((error) => console.error(error)); + }); }); describe("Call Automation Main Client Live Tests", function () { - let recorder: Recorder; - let callerCallAutomationClient: CallAutomationClient; - let receiverCallAutomationClient: CallAutomationClient; - let callConnection: CallConnection; - let testUser: CommunicationUserIdentifier; - let testUser2: CommunicationUserIdentifier; - let testName: string; - - beforeEach(async function (this: Context) { - recorder = await createRecorder(this.currentTest); - testUser = await createTestUser(recorder); - testUser2 = await createTestUser(recorder); - callerCallAutomationClient = createCallAutomationClient(recorder, testUser); - receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + let recorder: Recorder; + let callerCallAutomationClient: CallAutomationClient; + let receiverCallAutomationClient: CallAutomationClient; + let callConnection: CallConnection; + let testUser: CommunicationUserIdentifier; + let testUser2: CommunicationUserIdentifier; + let testName: string; + + beforeEach(async function (this: Context) { + recorder = await createRecorder(this.currentTest); + testUser = await createTestUser(recorder); + testUser2 = await createTestUser(recorder); + callerCallAutomationClient = createCallAutomationClient(recorder, testUser); + receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + }); + + afterEach(async function (this: Context) { + persistEvents(testName); + serviceBusReceivers.forEach((receiver) => { + receiver.close(); }); - - afterEach(async function (this: Context) { - persistEvents(testName); - serviceBusReceivers.forEach((receiver) => { - receiver.close(); - }); - events.forEach((callConnectionEvents) => { - callConnectionEvents.clear(); - }); - events.clear(); - serviceBusReceivers.clear(); - incomingCallContexts.clear(); - await recorder.stop(); - if (callConnection) { - try { - await callConnection.hangUp(true); - } catch { - return; - } - } + events.forEach((callConnectionEvents) => { + callConnectionEvents.clear(); }); - - it("Create a call and hangup", async function () { - testName = this.test?.fullTitle() - ? this.test?.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", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOptions, - ); - } - - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - + events.clear(); + serviceBusReceivers.clear(); + incomingCallContexts.clear(); + await recorder.stop(); + if (callConnection) { + try { await callConnection.hangUp(true); - - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Reject call", async function () { - testName = this.test?.fullTitle() ? this.test?.fullTitle().replace(/ /g, "_") : "reject_call"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "operationContextRejectCall" }; - - 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) { - await receiverCallAutomationClient.rejectCall(incomingCallContext); - } - - const CallDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(CallDisconnectedEvent); - }).timeout(60000); + } catch { + return; + } + } + }); + + it("Create a call and hangup", async function () { + testName = this.test?.fullTitle() + ? this.test?.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", + }; + 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); + }).timeout(60000); + + it("Reject call", async function () { + testName = this.test?.fullTitle() ? this.test?.fullTitle().replace(/ /g, "_") : "reject_call"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "operationContextRejectCall" }; + + 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) { + await receiverCallAutomationClient.rejectCall(incomingCallContext); + } + + const CallDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(CallDisconnectedEvent); + }).timeout(60000); }); diff --git a/sdk/communication/communication-call-automation/test/callConnection.spec.ts b/sdk/communication/communication-call-automation/test/callConnection.spec.ts index 649c79c683f9..2cdcc59bc454 100644 --- a/sdk/communication/communication-call-automation/test/callConnection.spec.ts +++ b/sdk/communication/communication-call-automation/test/callConnection.spec.ts @@ -6,612 +6,612 @@ import { CommunicationUserIdentifier } from "@azure/communication-common"; import { assert } from "chai"; import { Context } from "mocha"; import { - CallAutomationClient, - CallInvite, - CallConnection, - CallConnectionProperties, - CallParticipant, - ListParticipantsResult, - AddParticipantResult, - TransferCallResult, - RemoveParticipantResult, - MuteParticipantResult, - CancelAddParticipantOperationResult, - CancelAddParticipantSucceeded, - CreateCallOptions, - AnswerCallOptions, - AddParticipantOptions, - RemoveParticipantsOption, - CancelAddParticipantOperationOptions, + CallAutomationClient, + CallInvite, + CallConnection, + CallConnectionProperties, + CallParticipant, + ListParticipantsResult, + AddParticipantResult, + TransferCallResult, + RemoveParticipantResult, + MuteParticipantResult, + CancelAddParticipantOperationResult, + CancelAddParticipantSucceeded, + CreateCallOptions, + AnswerCallOptions, + AddParticipantOptions, + RemoveParticipantsOption, + CancelAddParticipantOperationOptions, } from "../src"; import Sinon, { SinonStubbedInstance } from "sinon"; import { CALL_TARGET_ID, CALL_TARGET_ID_2 } from "./utils/connectionUtils"; import { - createRecorder, - createTestUser, - dispatcherCallback, - serviceBusWithNewCall, - createCallAutomationClient, - waitForIncomingCallContext, - waitForEvent, - events, - serviceBusReceivers, - incomingCallContexts, - persistEvents, - loadPersistedEvents, + createRecorder, + createTestUser, + dispatcherCallback, + serviceBusWithNewCall, + createCallAutomationClient, + waitForIncomingCallContext, + waitForEvent, + events, + serviceBusReceivers, + incomingCallContexts, + persistEvents, + loadPersistedEvents, } from "./utils/recordedClient"; describe("CallConnection Unit Tests", () => { - let target: CallInvite; - let callConnection: SinonStubbedInstance & CallConnection; - - beforeEach(() => { - // set up - target = { - targetParticipant: { communicationUserId: CALL_TARGET_ID }, - }; - - // stub CallConnection - callConnection = Sinon.createStubInstance( - CallConnection, - ) as SinonStubbedInstance & CallConnection; - }); - - it("GetCallConnectionProperties", async () => { - // mocks - const callConnectionPropertiesMock: CallConnectionProperties = {}; - callConnection.getCallConnectionProperties.returns( - new Promise((resolve) => { - resolve(callConnectionPropertiesMock); - }), - ); - - const promiseResult = callConnection.getCallConnectionProperties(); - - // asserts - promiseResult - .then((result: CallConnectionProperties) => { - assert.isNotNull(result); - assert.isTrue(callConnection.getCallConnectionProperties.calledWith()); - assert.equal(result, callConnectionPropertiesMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("HangUp", async () => { - // mocks - callConnection.hangUp.returns( - new Promise((resolve) => { - resolve(undefined); - }), - ); - - const promiseResult = callConnection.hangUp(false); - - // asserts - promiseResult - .then(() => { - assert.isTrue(callConnection.hangUp.calledWith(false)); - return; - }) - .catch((error) => console.error(error)); - }); - - it("Terminate", async () => { - // mocks - callConnection.hangUp.returns( - new Promise((resolve) => { - resolve(undefined); - }), - ); - - const promiseResult = callConnection.hangUp(true); - - // asserts - promiseResult - .then(() => { - assert.isTrue(callConnection.hangUp.calledWith(true)); - return; - }) - .catch((error) => console.error(error)); - }); - - it("GetParticipant", async () => { - // mocks - const callParticipantMock: CallParticipant = {}; - callConnection.getParticipant.returns( - new Promise((resolve) => { - resolve(callParticipantMock); - }), - ); - - const promiseResult = callConnection.getParticipant(target.targetParticipant); - - // asserts - promiseResult - .then((result: CallParticipant) => { - assert.isNotNull(result); - assert.isTrue(callConnection.getParticipant.calledWith(target.targetParticipant)); - assert.equal(result, callParticipantMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("ListParticipants", async () => { - // mocks - const listParticipantsResultMock: ListParticipantsResult = {}; - callConnection.listParticipants.returns( - new Promise((resolve) => { - resolve(listParticipantsResultMock); - }), - ); - - const promiseResult = callConnection.listParticipants(); - - // asserts - promiseResult - .then((result: ListParticipantsResult) => { - assert.isNotNull(result); - assert.isTrue(callConnection.listParticipants.calledWith()); - assert.equal(result, listParticipantsResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("AddParticipant", async () => { - // mocks - const addParticipantResultMock: AddParticipantResult = {}; - callConnection.addParticipant.returns( - new Promise((resolve) => { - resolve(addParticipantResultMock); - }), - ); - - const promiseResult = callConnection.addParticipant(target); - - // asserts - promiseResult - .then((result: AddParticipantResult) => { - assert.isNotNull(result); - assert.isTrue(callConnection.addParticipant.calledWith(target)); - assert.equal(result, addParticipantResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("TransferCallToParticipant", async () => { - // mocks - const transferCallResultMock: TransferCallResult = {}; - callConnection.transferCallToParticipant.returns( - new Promise((resolve) => { - resolve(transferCallResultMock); - }), - ); - - const promiseResult = callConnection.transferCallToParticipant(target.targetParticipant); - - // asserts - promiseResult - .then((result: TransferCallResult) => { - assert.isNotNull(result); - assert.isTrue( - callConnection.transferCallToParticipant.calledWith(target.targetParticipant), - ); - assert.equal(result, transferCallResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("TransferCallToParticipantWithTransferee", async () => { - // mocks - const transferCallResultMock: TransferCallResult = {}; - callConnection.transferCallToParticipant.returns( - new Promise((resolve) => { - resolve(transferCallResultMock); - }), - ); - - const transferee = { communicationUserId: CALL_TARGET_ID_2 }; - - const promiseResult = callConnection.transferCallToParticipant(target.targetParticipant, { - transferee: transferee, - }); - - // asserts - promiseResult - .then((result: TransferCallResult) => { - assert.isNotNull(result); - assert.isTrue( - callConnection.transferCallToParticipant.calledWith(target.targetParticipant), - ); - assert.equal(result, transferCallResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("RemoveParticipant", async () => { - // mocks - const removeParticipantResultMock: RemoveParticipantResult = {}; - callConnection.removeParticipant.returns( - new Promise((resolve) => { - resolve(removeParticipantResultMock); - }), + let target: CallInvite; + let callConnection: SinonStubbedInstance & CallConnection; + + beforeEach(() => { + // set up + target = { + targetParticipant: { communicationUserId: CALL_TARGET_ID }, + }; + + // stub CallConnection + callConnection = Sinon.createStubInstance( + CallConnection, + ) as SinonStubbedInstance & CallConnection; + }); + + it("GetCallConnectionProperties", async () => { + // mocks + const callConnectionPropertiesMock: CallConnectionProperties = {}; + callConnection.getCallConnectionProperties.returns( + new Promise((resolve) => { + resolve(callConnectionPropertiesMock); + }), + ); + + const promiseResult = callConnection.getCallConnectionProperties(); + + // asserts + promiseResult + .then((result: CallConnectionProperties) => { + assert.isNotNull(result); + assert.isTrue(callConnection.getCallConnectionProperties.calledWith()); + assert.equal(result, callConnectionPropertiesMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("HangUp", async () => { + // mocks + callConnection.hangUp.returns( + new Promise((resolve) => { + resolve(undefined); + }), + ); + + const promiseResult = callConnection.hangUp(false); + + // asserts + promiseResult + .then(() => { + assert.isTrue(callConnection.hangUp.calledWith(false)); + return; + }) + .catch((error) => console.error(error)); + }); + + it("Terminate", async () => { + // mocks + callConnection.hangUp.returns( + new Promise((resolve) => { + resolve(undefined); + }), + ); + + const promiseResult = callConnection.hangUp(true); + + // asserts + promiseResult + .then(() => { + assert.isTrue(callConnection.hangUp.calledWith(true)); + return; + }) + .catch((error) => console.error(error)); + }); + + it("GetParticipant", async () => { + // mocks + const callParticipantMock: CallParticipant = {}; + callConnection.getParticipant.returns( + new Promise((resolve) => { + resolve(callParticipantMock); + }), + ); + + const promiseResult = callConnection.getParticipant(target.targetParticipant); + + // asserts + promiseResult + .then((result: CallParticipant) => { + assert.isNotNull(result); + assert.isTrue(callConnection.getParticipant.calledWith(target.targetParticipant)); + assert.equal(result, callParticipantMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("ListParticipants", async () => { + // mocks + const listParticipantsResultMock: ListParticipantsResult = {}; + callConnection.listParticipants.returns( + new Promise((resolve) => { + resolve(listParticipantsResultMock); + }), + ); + + const promiseResult = callConnection.listParticipants(); + + // asserts + promiseResult + .then((result: ListParticipantsResult) => { + assert.isNotNull(result); + assert.isTrue(callConnection.listParticipants.calledWith()); + assert.equal(result, listParticipantsResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("AddParticipant", async () => { + // mocks + const addParticipantResultMock: AddParticipantResult = {}; + callConnection.addParticipant.returns( + new Promise((resolve) => { + resolve(addParticipantResultMock); + }), + ); + + const promiseResult = callConnection.addParticipant(target); + + // asserts + promiseResult + .then((result: AddParticipantResult) => { + assert.isNotNull(result); + assert.isTrue(callConnection.addParticipant.calledWith(target)); + assert.equal(result, addParticipantResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("TransferCallToParticipant", async () => { + // mocks + const transferCallResultMock: TransferCallResult = {}; + callConnection.transferCallToParticipant.returns( + new Promise((resolve) => { + resolve(transferCallResultMock); + }), + ); + + const promiseResult = callConnection.transferCallToParticipant(target.targetParticipant); + + // asserts + promiseResult + .then((result: TransferCallResult) => { + assert.isNotNull(result); + assert.isTrue( + callConnection.transferCallToParticipant.calledWith(target.targetParticipant), ); - - const promiseResult = callConnection.removeParticipant(target.targetParticipant); - - // asserts - promiseResult - .then((result: RemoveParticipantResult) => { - assert.isNotNull(result); - assert.isTrue(callConnection.removeParticipant.calledWith(target.targetParticipant)); - assert.equal(result, removeParticipantResultMock); - return; - }) - .catch((error) => console.error(error)); + assert.equal(result, transferCallResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("TransferCallToParticipantWithTransferee", async () => { + // mocks + const transferCallResultMock: TransferCallResult = {}; + callConnection.transferCallToParticipant.returns( + new Promise((resolve) => { + resolve(transferCallResultMock); + }), + ); + + const transferee = { communicationUserId: CALL_TARGET_ID_2 }; + + const promiseResult = callConnection.transferCallToParticipant(target.targetParticipant, { + transferee: transferee, }); - it("MuteParticipant", async () => { - // mocks - const muteParticipantResultMock: MuteParticipantResult = {}; - callConnection.muteParticipant.returns( - new Promise((resolve) => { - resolve(muteParticipantResultMock); - }), + // asserts + promiseResult + .then((result: TransferCallResult) => { + assert.isNotNull(result); + assert.isTrue( + callConnection.transferCallToParticipant.calledWith(target.targetParticipant), ); - - const promiseResult = callConnection.muteParticipant(target.targetParticipant); - - // asserts - promiseResult - .then((result: MuteParticipantResult) => { - assert.isNotNull(result); - assert.isTrue(callConnection.muteParticipant.calledWith(target.targetParticipant)); - assert.equal(result, muteParticipantResultMock); - return; - }) - .catch((error) => console.error(error)); - }); - - it("cancelAddParticipantOperation", async () => { - const invitationId = "invitationId"; - const cancelAddParticipantOperationResultMock: CancelAddParticipantOperationResult = { - invitationId, - }; - callConnection.cancelAddParticipantOperation.returns( - new Promise((resolve) => { - resolve(cancelAddParticipantOperationResultMock); - }), - ); - - callConnection - .cancelAddParticipantOperation(invitationId) - .then((result: CancelAddParticipantOperationResult) => { - assert.isNotNull(result); - assert.isTrue(callConnection.cancelAddParticipantOperation.calledWith(invitationId)); - assert.equal(result, cancelAddParticipantOperationResultMock); - return; - }) - .catch((error) => console.error(error)); - }); + assert.equal(result, transferCallResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("RemoveParticipant", async () => { + // mocks + const removeParticipantResultMock: RemoveParticipantResult = {}; + callConnection.removeParticipant.returns( + new Promise((resolve) => { + resolve(removeParticipantResultMock); + }), + ); + + const promiseResult = callConnection.removeParticipant(target.targetParticipant); + + // asserts + promiseResult + .then((result: RemoveParticipantResult) => { + assert.isNotNull(result); + assert.isTrue(callConnection.removeParticipant.calledWith(target.targetParticipant)); + assert.equal(result, removeParticipantResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("MuteParticipant", async () => { + // mocks + const muteParticipantResultMock: MuteParticipantResult = {}; + callConnection.muteParticipant.returns( + new Promise((resolve) => { + resolve(muteParticipantResultMock); + }), + ); + + const promiseResult = callConnection.muteParticipant(target.targetParticipant); + + // asserts + promiseResult + .then((result: MuteParticipantResult) => { + assert.isNotNull(result); + assert.isTrue(callConnection.muteParticipant.calledWith(target.targetParticipant)); + assert.equal(result, muteParticipantResultMock); + return; + }) + .catch((error) => console.error(error)); + }); + + it("cancelAddParticipantOperation", async () => { + const invitationId = "invitationId"; + const cancelAddParticipantOperationResultMock: CancelAddParticipantOperationResult = { + invitationId, + }; + callConnection.cancelAddParticipantOperation.returns( + new Promise((resolve) => { + resolve(cancelAddParticipantOperationResultMock); + }), + ); + + callConnection + .cancelAddParticipantOperation(invitationId) + .then((result: CancelAddParticipantOperationResult) => { + assert.isNotNull(result); + assert.isTrue(callConnection.cancelAddParticipantOperation.calledWith(invitationId)); + assert.equal(result, cancelAddParticipantOperationResultMock); + return; + }) + .catch((error) => console.error(error)); + }); }); describe("CallConnection Live Tests", function () { - let recorder: Recorder; - let callerCallAutomationClient: CallAutomationClient; - let receiverCallAutomationClient: CallAutomationClient; - let callConnection: CallConnection; - let testUser: CommunicationUserIdentifier; - let testUser2: CommunicationUserIdentifier; - let callConnectionId: string; - let testName: string; - - beforeEach(async function (this: Context) { - recorder = await createRecorder(this.currentTest); - testUser = await createTestUser(recorder); - testUser2 = await createTestUser(recorder); - callerCallAutomationClient = createCallAutomationClient(recorder, testUser); - receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + let recorder: Recorder; + let callerCallAutomationClient: CallAutomationClient; + let receiverCallAutomationClient: CallAutomationClient; + let callConnection: CallConnection; + let testUser: CommunicationUserIdentifier; + let testUser2: CommunicationUserIdentifier; + let callConnectionId: string; + let testName: string; + + beforeEach(async function (this: Context) { + recorder = await createRecorder(this.currentTest); + testUser = await createTestUser(recorder); + testUser2 = await createTestUser(recorder); + callerCallAutomationClient = createCallAutomationClient(recorder, testUser); + receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + }); + + afterEach(async function (this: Context) { + persistEvents(testName); + if (callConnection) { + try { + await callConnection.hangUp(true); + } catch (e) { + console.log("Call is terminated"); + } + } + serviceBusReceivers.forEach((receiver) => { + receiver.close(); }); - - afterEach(async function (this: Context) { - persistEvents(testName); - if (callConnection) { - try { - await callConnection.hangUp(true); - } catch (e) { - console.log("Call is terminated"); - } - } - serviceBusReceivers.forEach((receiver) => { - receiver.close(); - }); - events.forEach((callConnectionEvents) => { - callConnectionEvents.clear(); - }); - events.clear(); - serviceBusReceivers.clear(); - incomingCallContexts.clear(); - await recorder.stop(); + events.forEach((callConnectionEvents) => { + callConnectionEvents.clear(); }); - - it("List all participants", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "list_all_participants"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "listParticipantsCreateCall" }; - - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); - callConnectionId = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - const answerCallOptions: AnswerCallOptions = { operationContext: "listParticipantsAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOptions, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - const allParticipants = await callConnection.listParticipants(); - assert.isDefined(allParticipants); - assert.isDefined(allParticipants.values); - }).timeout(60000); - - it("Add a participant and get call properties", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "add_participant_and_get_call_props"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "addParticipantsCreateCall" }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 10000); - callConnectionId = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - const answerCallOptions: AnswerCallOptions = { operationContext: "addParticipantsAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOptions, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 10000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - const testUser3: CommunicationUserIdentifier = await createTestUser(recorder); - const participantInvite: CallInvite = { targetParticipant: testUser3 }; - const uniqueId2 = await serviceBusWithNewCall(testUser, testUser3); - const callBackUrl2: string = dispatcherCallback + `?q=${uniqueId2}`; - - const addParticipantOptions: AddParticipantOptions = { operationContext: "addParticipants" }; - const addResult = await callConnection.addParticipant(participantInvite, addParticipantOptions); - assert.isDefined(addResult); - - const anotherReceiverCallAutomationClient: CallAutomationClient = createCallAutomationClient( - recorder, - testUser3, - ); - const anotherIncomingCallContext = await waitForIncomingCallContext(uniqueId2, 20000); - if (anotherIncomingCallContext) { - const answerCallOption2: AddParticipantOptions = { - operationContext: "addParticipantsAnswer2", - }; - await anotherReceiverCallAutomationClient.answerCall( - anotherIncomingCallContext, - callBackUrl2, - answerCallOption2, - ); - } - const participantAddedEvent = await waitForEvent( - "AddParticipantSucceeded", - callConnectionId, - 10000, - ); - assert.isDefined(participantAddedEvent); - - const callProperties = await callConnection.getCallConnectionProperties(); - assert.isDefined(callProperties); - }).timeout(90000); - - it("Remove a participant", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "remove_a_participant"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "removeParticipantCreateCall" }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); - callConnectionId = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - const answerCallOption: AnswerCallOptions = { operationContext: "removeParticipantsAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - const removeParticipantOptions: RemoveParticipantsOption = { - operationContext: "removeParticipants", - }; - const removeResult = await callConnection.removeParticipant( - testUser2, - removeParticipantOptions, - ); - assert.isDefined(removeResult); - - // A call needs at least 2 participants, removing one of the only 2 participants would end the call. - const callEndedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callEndedEvent); - }).timeout(60000); - - it("Mute a participant", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "add_participant_and_get_call_props"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 20000); - callConnectionId = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - const testUser3: CommunicationUserIdentifier = await createTestUser(recorder); - const participantInvite: CallInvite = { targetParticipant: testUser3 }; - const uniqueId2 = await serviceBusWithNewCall(testUser, testUser3); - const callBackUrl2: string = dispatcherCallback + `?q=${uniqueId2}`; - - const addParticipantOption: AddParticipantOptions = { operationContext: "addParticipant" }; - const addResult = await callConnection.addParticipant(participantInvite, addParticipantOption); - assert.isDefined(addResult); - - // A call needs at least 3 participants to mute a participant. So adding one more participant. - const anotherReceiverCallAutomationClient: CallAutomationClient = createCallAutomationClient( - recorder, - testUser3, - ); - const anotherIncomingCallContext = await waitForIncomingCallContext(uniqueId2, 20000); - if (anotherIncomingCallContext) { - await anotherReceiverCallAutomationClient.answerCall( - anotherIncomingCallContext, - callBackUrl2, - ); - } - const participantAddedEvent = await waitForEvent( - "AddParticipantSucceeded", - callConnectionId, - 8000, - ); - assert.isDefined(participantAddedEvent); - - const muteResult = await callConnection.muteParticipant(testUser2); - assert.isDefined(muteResult); - - await new Promise((resolve) => setTimeout(resolve, 3000)); - - const participantLists = await callConnection.listParticipants(); - let isMuted = false; - for (const participant of participantLists.values!) { - const communicationUser = participant.identifier as CommunicationUserIdentifier; - if (communicationUser.communicationUserId === testUser2.communicationUserId) { - isMuted = participant.isMuted!; - } - } - assert.isTrue(isMuted); - }).timeout(90000); - - it("Add a participant cancels add participant request", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "cancel_add_participant"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "cancelAddCreateCall" }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 10000); - callConnectionId = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - const answerCallOption: AnswerCallOptions = { operationContext: "cancelAddCreateAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 10000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - const testUser3: CommunicationUserIdentifier = await createTestUser(recorder); - const participantInvite: CallInvite = { targetParticipant: testUser3 }; - - const addParticipantOptions: AddParticipantOptions = { operationContext: "cancelAdd" }; - const addResult = await callConnection.addParticipant(participantInvite, addParticipantOptions); - assert.isDefined(addResult); - - // ensure invitation is sent out - await ((ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)))(3000); - - // cancel add participant - const cancelParticipantOption: CancelAddParticipantOperationOptions = { - operationContext: "cancelOp", - }; - await callConnection.cancelAddParticipantOperation( - addResult.invitationId!, - cancelParticipantOption, - ); - - const addParticipantCancelledEvent = (await waitForEvent( - "CancelAddParticipantSucceeded", - callConnectionId, - 10000, - )) as CancelAddParticipantSucceeded; - - assert.isDefined(addParticipantCancelledEvent); - assert.equal(addResult.invitationId, addParticipantCancelledEvent?.invitationId); - }).timeout(90000); + events.clear(); + serviceBusReceivers.clear(); + incomingCallContexts.clear(); + await recorder.stop(); + }); + + it("List all participants", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "list_all_participants"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "listParticipantsCreateCall" }; + + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + callConnectionId = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + const answerCallOptions: AnswerCallOptions = { operationContext: "listParticipantsAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOptions, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + const allParticipants = await callConnection.listParticipants(); + assert.isDefined(allParticipants); + assert.isDefined(allParticipants.values); + }).timeout(60000); + + it("Add a participant and get call properties", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "add_participant_and_get_call_props"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "addParticipantsCreateCall" }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 10000); + callConnectionId = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + const answerCallOptions: AnswerCallOptions = { operationContext: "addParticipantsAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOptions, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 10000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + const testUser3: CommunicationUserIdentifier = await createTestUser(recorder); + const participantInvite: CallInvite = { targetParticipant: testUser3 }; + const uniqueId2 = await serviceBusWithNewCall(testUser, testUser3); + const callBackUrl2: string = dispatcherCallback + `?q=${uniqueId2}`; + + const addParticipantOptions: AddParticipantOptions = { operationContext: "addParticipants" }; + const addResult = await callConnection.addParticipant(participantInvite, addParticipantOptions); + assert.isDefined(addResult); + + const anotherReceiverCallAutomationClient: CallAutomationClient = createCallAutomationClient( + recorder, + testUser3, + ); + const anotherIncomingCallContext = await waitForIncomingCallContext(uniqueId2, 20000); + if (anotherIncomingCallContext) { + const answerCallOption2: AddParticipantOptions = { + operationContext: "addParticipantsAnswer2", + }; + await anotherReceiverCallAutomationClient.answerCall( + anotherIncomingCallContext, + callBackUrl2, + answerCallOption2, + ); + } + const participantAddedEvent = await waitForEvent( + "AddParticipantSucceeded", + callConnectionId, + 10000, + ); + assert.isDefined(participantAddedEvent); + + const callProperties = await callConnection.getCallConnectionProperties(); + assert.isDefined(callProperties); + }).timeout(90000); + + it("Remove a participant", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "remove_a_participant"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "removeParticipantCreateCall" }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + callConnectionId = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + const answerCallOption: AnswerCallOptions = { operationContext: "removeParticipantsAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + const removeParticipantOptions: RemoveParticipantsOption = { + operationContext: "removeParticipants", + }; + const removeResult = await callConnection.removeParticipant( + testUser2, + removeParticipantOptions, + ); + assert.isDefined(removeResult); + + // A call needs at least 2 participants, removing one of the only 2 participants would end the call. + const callEndedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callEndedEvent); + }).timeout(60000); + + it("Mute a participant", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "add_participant_and_get_call_props"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 20000); + callConnectionId = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + const testUser3: CommunicationUserIdentifier = await createTestUser(recorder); + const participantInvite: CallInvite = { targetParticipant: testUser3 }; + const uniqueId2 = await serviceBusWithNewCall(testUser, testUser3); + const callBackUrl2: string = dispatcherCallback + `?q=${uniqueId2}`; + + const addParticipantOption: AddParticipantOptions = { operationContext: "addParticipant" }; + const addResult = await callConnection.addParticipant(participantInvite, addParticipantOption); + assert.isDefined(addResult); + + // A call needs at least 3 participants to mute a participant. So adding one more participant. + const anotherReceiverCallAutomationClient: CallAutomationClient = createCallAutomationClient( + recorder, + testUser3, + ); + const anotherIncomingCallContext = await waitForIncomingCallContext(uniqueId2, 20000); + if (anotherIncomingCallContext) { + await anotherReceiverCallAutomationClient.answerCall( + anotherIncomingCallContext, + callBackUrl2, + ); + } + const participantAddedEvent = await waitForEvent( + "AddParticipantSucceeded", + callConnectionId, + 8000, + ); + assert.isDefined(participantAddedEvent); + + const muteResult = await callConnection.muteParticipant(testUser2); + assert.isDefined(muteResult); + + await new Promise((resolve) => setTimeout(resolve, 3000)); + + const participantLists = await callConnection.listParticipants(); + let isMuted = false; + for (const participant of participantLists.values!) { + const communicationUser = participant.identifier as CommunicationUserIdentifier; + if (communicationUser.communicationUserId === testUser2.communicationUserId) { + isMuted = participant.isMuted!; + } + } + assert.isTrue(isMuted); + }).timeout(90000); + + it("Add a participant cancels add participant request", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "cancel_add_participant"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "cancelAddCreateCall" }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 10000); + callConnectionId = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + const answerCallOption: AnswerCallOptions = { operationContext: "cancelAddCreateAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 10000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + const testUser3: CommunicationUserIdentifier = await createTestUser(recorder); + const participantInvite: CallInvite = { targetParticipant: testUser3 }; + + const addParticipantOptions: AddParticipantOptions = { operationContext: "cancelAdd" }; + const addResult = await callConnection.addParticipant(participantInvite, addParticipantOptions); + assert.isDefined(addResult); + + // ensure invitation is sent out + await ((ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)))(3000); + + // cancel add participant + const cancelParticipantOption: CancelAddParticipantOperationOptions = { + operationContext: "cancelOp", + }; + await callConnection.cancelAddParticipantOperation( + addResult.invitationId!, + cancelParticipantOption, + ); + + const addParticipantCancelledEvent = (await waitForEvent( + "CancelAddParticipantSucceeded", + callConnectionId, + 10000, + )) as CancelAddParticipantSucceeded; + + assert.isDefined(addParticipantCancelledEvent); + assert.equal(addResult.invitationId, addParticipantCancelledEvent?.invitationId); + }).timeout(90000); }); diff --git a/sdk/communication/communication-call-automation/test/callMediaClient.spec.ts b/sdk/communication/communication-call-automation/test/callMediaClient.spec.ts index 0ce967ef60b7..f6625bcb8138 100644 --- a/sdk/communication/communication-call-automation/test/callMediaClient.spec.ts +++ b/sdk/communication/communication-call-automation/test/callMediaClient.spec.ts @@ -6,2140 +6,2140 @@ import { Context } from "mocha"; // Internal module imports import { Recorder } from "@azure-tools/test-recorder"; import { - CommunicationIdentifier, - CommunicationUserIdentifier, - PhoneNumberIdentifier, - serializeCommunicationIdentifier, + CommunicationIdentifier, + CommunicationUserIdentifier, + PhoneNumberIdentifier, + serializeCommunicationIdentifier, } from "@azure/communication-common"; // Parent directory imports import { CallMedia } from "../src/callMedia"; import { - FileSource, - TextSource, - SsmlSource, - RecognitionChoice, - DtmfTone, - MediaStreamingOptions, - TranscriptionOptions, - CallParticipant, + FileSource, + TextSource, + SsmlSource, + RecognitionChoice, + DtmfTone, + MediaStreamingOptions, + TranscriptionOptions, + CallParticipant, } from "../src/models/models"; import { - CallMediaRecognizeDtmfOptions, - CallMediaRecognizeChoiceOptions, - CallMediaRecognizeSpeechOptions, - CallAutomationClient, - CallConnection, - CallInvite, - ContinuousDtmfRecognitionOptions, - SendDtmfTonesOptions, - CreateCallOptions, - AnswerCallOptions, - PlayOptions, - StartTranscriptionOptions, - StopTranscriptionOptions, - HoldOptions, - UnholdOptions, - PlayToAllOptions, - StopMediaStreamingOptions, - StartMediaStreamingOptions, - CallMediaRecognizeSpeechOrDtmfOptions, + CallMediaRecognizeDtmfOptions, + CallMediaRecognizeChoiceOptions, + CallMediaRecognizeSpeechOptions, + CallAutomationClient, + CallConnection, + CallInvite, + ContinuousDtmfRecognitionOptions, + SendDtmfTonesOptions, + CreateCallOptions, + AnswerCallOptions, + PlayOptions, + StartTranscriptionOptions, + StopTranscriptionOptions, + HoldOptions, + UnholdOptions, + PlayToAllOptions, + StopMediaStreamingOptions, + StartMediaStreamingOptions, + CallMediaRecognizeSpeechOrDtmfOptions, } from "../src"; // Current directory imports import { - createRecorder, - createTestUser, - dispatcherCallback, - serviceBusWithNewCall, - createCallAutomationClient, - waitForIncomingCallContext, - waitForEvent, - events, - serviceBusReceivers, - incomingCallContexts, - loadPersistedEvents, - persistEvents, - fileSourceUrl, - getPhoneNumbers, - transportUrl, - cognitiveServiceEndpoint, + createRecorder, + createTestUser, + dispatcherCallback, + serviceBusWithNewCall, + createCallAutomationClient, + waitForIncomingCallContext, + waitForEvent, + events, + serviceBusReceivers, + incomingCallContexts, + loadPersistedEvents, + persistEvents, + fileSourceUrl, + getPhoneNumbers, + transportUrl, + cognitiveServiceEndpoint, } from "./utils/recordedClient"; import sinon from "sinon"; import { assert } from "chai"; import { createMediaClient, generateHttpClient } from "./utils/mockClient"; import { - CALL_CONNECTION_ID, - CALL_TARGET_ID, - MEDIA_UR_MP3, - MEDIA_URL_WAV, - baseUri, - generateToken, + CALL_CONNECTION_ID, + CALL_TARGET_ID, + MEDIA_UR_MP3, + MEDIA_URL_WAV, + baseUri, + generateToken, } from "./utils/connectionUtils"; describe("CallMedia Unit Tests", async function () { - let callMedia: CallMedia; - - afterEach(function () { - sinon.restore(); - }); - - it("can instantiate", async function () { - new CallMedia(CALL_CONNECTION_ID, baseUri, { key: generateToken() }); - }); - - it("makes successful Play file request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: FileSource[] = [ - { - url: MEDIA_UR_MP3, - kind: "fileSource", - }, - ]; - - const playTo: CommunicationIdentifier[] = [{ communicationUserId: CALL_TARGET_ID }]; - - await callMedia.play(playSource, playTo); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.playTo[0].rawId, CALL_TARGET_ID); - assert.equal(data.playSources[0].kind, "file"); - assert.equal(data.playSources[0].file.uri, playSource[0].url); - assert.equal(request.method, "POST"); - }); - - it("makes successful Play TTS request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: TextSource[] = [ - { - text: "test test test", - customVoiceEndpointId: "customVoiceEndpointId", - kind: "textSource", - }, - ]; - - const playTo: CommunicationIdentifier[] = [{ communicationUserId: CALL_TARGET_ID }]; - - await callMedia.play(playSource, playTo); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.playTo[0].rawId, CALL_TARGET_ID); - assert.equal(data.playSources[0].kind, "text"); - assert.equal(data.playSources[0].text.text, playSource[0].text); - assert.equal(request.method, "POST"); - }); - - it("makes successful Play SSML request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: SsmlSource[] = [ - { - ssmlText: - 'Recognize Choice Completed, played through SSML source.', - customVoiceEndpointId: "customVoiceEndpointId", - kind: "ssmlSource", - }, - ]; - - const playTo: CommunicationIdentifier[] = [{ communicationUserId: CALL_TARGET_ID }]; - - await callMedia.play(playSource, playTo); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.playTo[0].rawId, CALL_TARGET_ID); - assert.equal(data.playSources[0].kind, "ssml"); - assert.equal(data.playSources[0].ssml.ssmlText, playSource[0].ssmlText); - assert.equal(request.method, "POST"); - }); - - it("makes successful PlayToAll request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: FileSource[] = [ - { - url: MEDIA_URL_WAV, - kind: "fileSource", - }, - ]; - - const playTo: CommunicationIdentifier[] = []; - - await callMedia.play(playSource, playTo); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.playSources[0].kind, "file"); - assert.equal(data.playSources[0].file.uri, playSource[0].url); - assert.equal(request.method, "POST"); - }); - - it("makes successful PlayToAll barge in request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: FileSource[] = [ - { - url: MEDIA_URL_WAV, - kind: "fileSource", - }, - ]; - - const options: PlayToAllOptions = { - interruptCallMediaOperation: true, - operationContext: "interruptMediaContext", - }; - - await callMedia.playToAll(playSource, options); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.playSources[0].kind, "file"); - assert.equal(data.playSources[0].file.uri, playSource[0].url); - assert.equal(request.method, "POST"); - assert.equal(data.operationContext, options.operationContext); - assert.equal(data.interruptCallMediaOperation, options.interruptCallMediaOperation); - }); - - it("makes successful PlayToAll barge in request with PlayOptions instead of PlayToAllOptions", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: FileSource[] = [ - { - url: MEDIA_URL_WAV, - kind: "fileSource", - }, - ]; - - const options: PlayOptions = { - operationContext: "interruptMediaContext", - }; - - await callMedia.playToAll(playSource, options); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.playSources[0].kind, "file"); - assert.equal(data.playSources[0].file.uri, playSource[0].url); - assert.equal(request.method, "POST"); - assert.equal(data.operationContext, options.operationContext); - assert.equal(data.interruptCallMediaOperation, false); - }); - - it("makes successful StartRecognizing DTMF request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const recognizeOptions: CallMediaRecognizeDtmfOptions = { - kind: "callMediaRecognizeDtmfOptions", - maxTonesToCollect: 5, - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "dtmf"); - assert.equal(data.recognizeOptions.dtmfOptions.maxTonesToCollect, 5); - assert.equal(request.method, "POST"); - }); - - it("makes successful StartRecognizing Choices request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const choice: RecognitionChoice = { - label: "choice", - phrases: ["test"], - }; - const recognizeOptions: CallMediaRecognizeChoiceOptions = { - choices: [choice], - kind: "callMediaRecognizeChoiceOptions", - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "choices"); - assert.equal(data.recognizeOptions.choices[0].phrases[0], "test"); - assert.equal(request.method, "POST"); - }); - - it("makes successful StartRecognizing Speech request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const recognizeOptions: CallMediaRecognizeSpeechOptions = { - kind: "callMediaRecognizeSpeechOptions", - speechRecognitionModelEndpointId: "customModelEndpointId", - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "speech"); - assert.equal(data.recognizeOptions.speechOptions.endSilenceTimeoutInMs, 2000); - assert.equal(request.method, "POST"); - }); - - it("makes successful StartRecognizing DTMF Prompts request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - - const prompts: (FileSource | TextSource | SsmlSource)[] = [ - { kind: "fileSource", url: MEDIA_URL_WAV }, - { kind: "textSource", text: "test" }, - { kind: "ssmlSource", ssmlText: "test" }, - ]; - const recognizeOptions: CallMediaRecognizeDtmfOptions = { - playPrompts: prompts, - kind: "callMediaRecognizeDtmfOptions", - maxTonesToCollect: 5, - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "dtmf"); - assert.equal(data.recognizeOptions.dtmfOptions.maxTonesToCollect, 5); - assert.equal(data.playPrompts.length, 3); - assert.equal(data.playPrompts[0].kind, "file"); - assert.equal(data.playPrompts[1].kind, "text"); - assert.equal(data.playPrompts[2].kind, "ssml"); - assert.equal(request.method, "POST"); - }); - - it("makes successful StartRecognizing Choices Prompts request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const choice: RecognitionChoice = { - label: "choice", - phrases: ["test"], - }; - - const prompts: (FileSource | TextSource | SsmlSource)[] = [ - { kind: "fileSource", url: MEDIA_URL_WAV }, - { kind: "textSource", text: "test" }, - { kind: "ssmlSource", ssmlText: "test" }, - ]; - - const recognizeOptions: CallMediaRecognizeChoiceOptions = { - playPrompts: prompts, - choices: [choice], - kind: "callMediaRecognizeChoiceOptions", - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "choices"); - assert.equal(data.recognizeOptions.choices[0].phrases[0], "test"); - assert.equal(data.playPrompts.length, 3); - assert.equal(data.playPrompts[0].kind, "file"); - assert.equal(data.playPrompts[1].kind, "text"); - assert.equal(data.playPrompts[2].kind, "ssml"); - assert.equal(request.method, "POST"); - }); - - it("makes successful StartRecognizing Speech Prompts request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - - const prompts: (FileSource | TextSource | SsmlSource)[] = [ - { kind: "fileSource", url: MEDIA_URL_WAV }, - { kind: "textSource", text: "test" }, - { kind: "ssmlSource", ssmlText: "test" }, - ]; - - const recognizeOptions: CallMediaRecognizeSpeechOptions = { - playPrompts: prompts, - kind: "callMediaRecognizeSpeechOptions", - speechRecognitionModelEndpointId: "customModelEndpointId", - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "speech"); - assert.equal(data.recognizeOptions.speechOptions.endSilenceTimeoutInMs, 2000); - assert.equal(data.playPrompts.length, 3); - assert.equal(data.playPrompts[0].kind, "file"); - assert.equal(data.playPrompts[1].kind, "text"); - assert.equal(data.playPrompts[2].kind, "ssml"); - assert.equal(request.method, "POST"); - }); - - it("makes successful StartRecognizing SpeechOrDTMF With DTMF Prompts request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - - const prompts: (FileSource | TextSource | SsmlSource)[] = [ - { kind: "fileSource", url: MEDIA_URL_WAV }, - { kind: "textSource", text: "test" }, - { kind: "ssmlSource", ssmlText: "test" }, - ]; - - const recognizeOptions: CallMediaRecognizeSpeechOrDtmfOptions = { - playPrompts: prompts, - maxTonesToCollect: 5, - kind: "callMediaRecognizeSpeechOrDtmfOptions", - speechRecognitionModelEndpointId: "customModelEndpointId", - }; - - await callMedia.startRecognizing(targetParticipant, recognizeOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.recognizeInputType, "speechOrDtmf"); - assert.equal(data.recognizeOptions.dtmfOptions.maxTonesToCollect, 5); - assert.equal(data.playPrompts.length, 3); - assert.equal(data.playPrompts[0].kind, "file"); - assert.equal(data.playPrompts[1].kind, "text"); - assert.equal(data.playPrompts[2].kind, "ssml"); - assert.equal(request.method, "POST"); - }); - - it("makes successful CancelAllMediaOperations request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - await callMedia.cancelAllOperations(); - - const request = spy.getCall(0).args[0]; - - assert.equal(request.method, "POST"); - }); - - it("makes successful StartContinuousDtmfRecognition request", async function () { - const mockHttpClient = generateHttpClient(200); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const continuousDtmfRecognitionOptions: ContinuousDtmfRecognitionOptions = { - operationContext: "test_operation_context", - }; - - await callMedia.startContinuousDtmfRecognition( - targetParticipant, - continuousDtmfRecognitionOptions, - ); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.deepEqual(data.targetParticipant, serializeCommunicationIdentifier(targetParticipant)); - assert.equal(data.operationContext, continuousDtmfRecognitionOptions.operationContext); - assert.equal(request.method, "POST"); - }); - - it("makes successful StopContinuousDtmfRecognition request", async function () { - const mockHttpClient = generateHttpClient(200); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const continuousDtmfRecognitionOptions: ContinuousDtmfRecognitionOptions = { - operationContext: "test_operation_context", - }; - - await callMedia.stopContinuousDtmfRecognition( - targetParticipant, - continuousDtmfRecognitionOptions, - ); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.deepEqual(data.targetParticipant, serializeCommunicationIdentifier(targetParticipant)); - assert.equal(data.operationContext, continuousDtmfRecognitionOptions.operationContext); - assert.equal(request.method, "POST"); - }); - - it("makes successful SendDtmf request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const sendDtmfOptions: SendDtmfTonesOptions = { - operationContext: "test_operation_context", - }; - const tones = ["one", "two", "three", "pound"]; - - await callMedia.sendDtmfTones(tones, targetParticipant, sendDtmfOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.deepEqual(data.targetParticipant, serializeCommunicationIdentifier(targetParticipant)); - assert.deepEqual(data.tones, tones); - assert.equal(data.operationContext, sendDtmfOptions.operationContext); - assert.equal(request.method, "POST"); - }); - - it("makes successful hold request", async function () { - const mockHttpClient = generateHttpClient(200); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const playSource: TextSource = { - text: "test test test", - customVoiceEndpointId: "customVoiceEndpointId", - kind: "textSource", - }; - - const participantToHold: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const options: HoldOptions = { - playSource: playSource, - operationContext: "withPlaySource", - operationCallbackUrl: "https://localhost", - }; - await callMedia.hold(participantToHold, options); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.equal(data.targetParticipant.rawId, CALL_TARGET_ID); - assert.equal(data.playSourceInfo.kind, "text"); - assert.equal(data.playSourceInfo.text.text, playSource.text); - assert.equal(data.operationContext, "withPlaySource"); - assert.equal(data.operationCallbackUri, "https://localhost"); - assert.equal(request.method, "POST"); - }); - - it("makes successful Hold request with no playSource", async function () { - const mockHttpClient = generateHttpClient(200); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const participantToHold: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - await callMedia.hold(participantToHold); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.equal(data.targetParticipant.rawId, CALL_TARGET_ID); - assert.equal(request.method, "POST"); - assert.isUndefined(data.playSourceInfo); - }); - - it("makes successful unhold request", async function () { - const mockHttpClient = generateHttpClient(200); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const participantToUnhold: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const options: UnholdOptions = { - operationContext: "unholdContext", - }; - await callMedia.unhold(participantToUnhold, options); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.equal(data.targetParticipant.rawId, CALL_TARGET_ID); - assert.equal(request.method, "POST"); - assert.equal(data.operationContext, options.operationContext); - }); - - it("makes successful Start Transcription request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const startTranscriptionOptions: StartTranscriptionOptions = { - locale: "en-US", - operationContext: "test_operation_context", - }; - - await callMedia.startTranscription(startTranscriptionOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.locale, startTranscriptionOptions.locale); - assert.equal(data.operationContext, startTranscriptionOptions.operationContext); - assert.equal(request.method, "POST"); - }); - - it("makes successful Stop TranscriptionOptions request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const stopTranscriptionOptions: StopTranscriptionOptions = { - operationContext: "test_operation_context", - }; - - await callMedia.stopTranscription(stopTranscriptionOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.operationContext, stopTranscriptionOptions.operationContext); - assert.equal(request.method, "POST"); - }); - - it("makes successful Update Transcription request", async function () { - const mockHttpClient = generateHttpClient(202); - - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const locale = "en-US"; - - await callMedia.updateTranscription(locale); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.locale, locale); - assert.equal(request.method, "POST"); - }); - - it("makes successful start media streaming request with options", async function () { - const mockHttpClient = generateHttpClient(202); - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const options: StartMediaStreamingOptions = { - operationContext: "startMediaStreamContext", - operationCallbackUrl: "https://localhost", - }; - await callMedia.startMediaStreaming(options); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.equal(data.operationContext, options.operationContext); - assert.equal(data.operationCallbackUri, options.operationCallbackUrl); - assert.equal(request.method, "POST"); - }); - - it("makes successful start media streaming request without options", async function () { - const mockHttpClient = generateHttpClient(202); - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - await callMedia.startMediaStreaming(); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.isUndefined(data.operationContext); - assert.isUndefined(data.operationCallbackUri); - assert.equal(request.method, "POST"); - }); - - it("makes successful stop media streaming request with options", async function () { - const mockHttpClient = generateHttpClient(202); - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - const options: StopMediaStreamingOptions = { - operationCallbackUrl: "https://localhost", - }; - await callMedia.stopMediaStreaming(options); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.equal(data.operationCallbackUri, options.operationCallbackUrl); - assert.equal(request.method, "POST"); - }); - - it("makes successful stop media streaming request without options", async function () { - const mockHttpClient = generateHttpClient(202); - callMedia = createMediaClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - await callMedia.stopMediaStreaming(); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - assert.isUndefined(data.operationCallbackUri); - assert.equal(request.method, "POST"); - }); + let callMedia: CallMedia; + + afterEach(function () { + sinon.restore(); + }); + + it("can instantiate", async function () { + new CallMedia(CALL_CONNECTION_ID, baseUri, { key: generateToken() }); + }); + + it("makes successful Play file request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: FileSource[] = [ + { + url: MEDIA_UR_MP3, + kind: "fileSource", + }, + ]; + + const playTo: CommunicationIdentifier[] = [{ communicationUserId: CALL_TARGET_ID }]; + + await callMedia.play(playSource, playTo); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.playTo[0].rawId, CALL_TARGET_ID); + assert.equal(data.playSources[0].kind, "file"); + assert.equal(data.playSources[0].file.uri, playSource[0].url); + assert.equal(request.method, "POST"); + }); + + it("makes successful Play TTS request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: TextSource[] = [ + { + text: "test test test", + customVoiceEndpointId: "customVoiceEndpointId", + kind: "textSource", + }, + ]; + + const playTo: CommunicationIdentifier[] = [{ communicationUserId: CALL_TARGET_ID }]; + + await callMedia.play(playSource, playTo); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.playTo[0].rawId, CALL_TARGET_ID); + assert.equal(data.playSources[0].kind, "text"); + assert.equal(data.playSources[0].text.text, playSource[0].text); + assert.equal(request.method, "POST"); + }); + + it("makes successful Play SSML request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: SsmlSource[] = [ + { + ssmlText: + 'Recognize Choice Completed, played through SSML source.', + customVoiceEndpointId: "customVoiceEndpointId", + kind: "ssmlSource", + }, + ]; + + const playTo: CommunicationIdentifier[] = [{ communicationUserId: CALL_TARGET_ID }]; + + await callMedia.play(playSource, playTo); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.playTo[0].rawId, CALL_TARGET_ID); + assert.equal(data.playSources[0].kind, "ssml"); + assert.equal(data.playSources[0].ssml.ssmlText, playSource[0].ssmlText); + assert.equal(request.method, "POST"); + }); + + it("makes successful PlayToAll request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: FileSource[] = [ + { + url: MEDIA_URL_WAV, + kind: "fileSource", + }, + ]; + + const playTo: CommunicationIdentifier[] = []; + + await callMedia.play(playSource, playTo); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.playSources[0].kind, "file"); + assert.equal(data.playSources[0].file.uri, playSource[0].url); + assert.equal(request.method, "POST"); + }); + + it("makes successful PlayToAll barge in request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: FileSource[] = [ + { + url: MEDIA_URL_WAV, + kind: "fileSource", + }, + ]; + + const options: PlayToAllOptions = { + interruptCallMediaOperation: true, + operationContext: "interruptMediaContext", + }; + + await callMedia.playToAll(playSource, options); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.playSources[0].kind, "file"); + assert.equal(data.playSources[0].file.uri, playSource[0].url); + assert.equal(request.method, "POST"); + assert.equal(data.operationContext, options.operationContext); + assert.equal(data.interruptCallMediaOperation, options.interruptCallMediaOperation); + }); + + it("makes successful PlayToAll barge in request with PlayOptions instead of PlayToAllOptions", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: FileSource[] = [ + { + url: MEDIA_URL_WAV, + kind: "fileSource", + }, + ]; + + const options: PlayOptions = { + operationContext: "interruptMediaContext", + }; + + await callMedia.playToAll(playSource, options); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.playSources[0].kind, "file"); + assert.equal(data.playSources[0].file.uri, playSource[0].url); + assert.equal(request.method, "POST"); + assert.equal(data.operationContext, options.operationContext); + assert.equal(data.interruptCallMediaOperation, false); + }); + + it("makes successful StartRecognizing DTMF request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const recognizeOptions: CallMediaRecognizeDtmfOptions = { + kind: "callMediaRecognizeDtmfOptions", + maxTonesToCollect: 5, + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "dtmf"); + assert.equal(data.recognizeOptions.dtmfOptions.maxTonesToCollect, 5); + assert.equal(request.method, "POST"); + }); + + it("makes successful StartRecognizing Choices request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const choice: RecognitionChoice = { + label: "choice", + phrases: ["test"], + }; + const recognizeOptions: CallMediaRecognizeChoiceOptions = { + choices: [choice], + kind: "callMediaRecognizeChoiceOptions", + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "choices"); + assert.equal(data.recognizeOptions.choices[0].phrases[0], "test"); + assert.equal(request.method, "POST"); + }); + + it("makes successful StartRecognizing Speech request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const recognizeOptions: CallMediaRecognizeSpeechOptions = { + kind: "callMediaRecognizeSpeechOptions", + speechRecognitionModelEndpointId: "customModelEndpointId", + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "speech"); + assert.equal(data.recognizeOptions.speechOptions.endSilenceTimeoutInMs, 2000); + assert.equal(request.method, "POST"); + }); + + it("makes successful StartRecognizing DTMF Prompts request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + + const prompts: (FileSource | TextSource | SsmlSource)[] = [ + { kind: "fileSource", url: MEDIA_URL_WAV }, + { kind: "textSource", text: "test" }, + { kind: "ssmlSource", ssmlText: "test" }, + ]; + const recognizeOptions: CallMediaRecognizeDtmfOptions = { + playPrompts: prompts, + kind: "callMediaRecognizeDtmfOptions", + maxTonesToCollect: 5, + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "dtmf"); + assert.equal(data.recognizeOptions.dtmfOptions.maxTonesToCollect, 5); + assert.equal(data.playPrompts.length, 3); + assert.equal(data.playPrompts[0].kind, "file"); + assert.equal(data.playPrompts[1].kind, "text"); + assert.equal(data.playPrompts[2].kind, "ssml"); + assert.equal(request.method, "POST"); + }); + + it("makes successful StartRecognizing Choices Prompts request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const choice: RecognitionChoice = { + label: "choice", + phrases: ["test"], + }; + + const prompts: (FileSource | TextSource | SsmlSource)[] = [ + { kind: "fileSource", url: MEDIA_URL_WAV }, + { kind: "textSource", text: "test" }, + { kind: "ssmlSource", ssmlText: "test" }, + ]; + + const recognizeOptions: CallMediaRecognizeChoiceOptions = { + playPrompts: prompts, + choices: [choice], + kind: "callMediaRecognizeChoiceOptions", + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "choices"); + assert.equal(data.recognizeOptions.choices[0].phrases[0], "test"); + assert.equal(data.playPrompts.length, 3); + assert.equal(data.playPrompts[0].kind, "file"); + assert.equal(data.playPrompts[1].kind, "text"); + assert.equal(data.playPrompts[2].kind, "ssml"); + assert.equal(request.method, "POST"); + }); + + it("makes successful StartRecognizing Speech Prompts request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + + const prompts: (FileSource | TextSource | SsmlSource)[] = [ + { kind: "fileSource", url: MEDIA_URL_WAV }, + { kind: "textSource", text: "test" }, + { kind: "ssmlSource", ssmlText: "test" }, + ]; + + const recognizeOptions: CallMediaRecognizeSpeechOptions = { + playPrompts: prompts, + kind: "callMediaRecognizeSpeechOptions", + speechRecognitionModelEndpointId: "customModelEndpointId", + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "speech"); + assert.equal(data.recognizeOptions.speechOptions.endSilenceTimeoutInMs, 2000); + assert.equal(data.playPrompts.length, 3); + assert.equal(data.playPrompts[0].kind, "file"); + assert.equal(data.playPrompts[1].kind, "text"); + assert.equal(data.playPrompts[2].kind, "ssml"); + assert.equal(request.method, "POST"); + }); + + it("makes successful StartRecognizing SpeechOrDTMF With DTMF Prompts request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + + const prompts: (FileSource | TextSource | SsmlSource)[] = [ + { kind: "fileSource", url: MEDIA_URL_WAV }, + { kind: "textSource", text: "test" }, + { kind: "ssmlSource", ssmlText: "test" }, + ]; + + const recognizeOptions: CallMediaRecognizeSpeechOrDtmfOptions = { + playPrompts: prompts, + maxTonesToCollect: 5, + kind: "callMediaRecognizeSpeechOrDtmfOptions", + speechRecognitionModelEndpointId: "customModelEndpointId", + }; + + await callMedia.startRecognizing(targetParticipant, recognizeOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.recognizeInputType, "speechOrDtmf"); + assert.equal(data.recognizeOptions.dtmfOptions.maxTonesToCollect, 5); + assert.equal(data.playPrompts.length, 3); + assert.equal(data.playPrompts[0].kind, "file"); + assert.equal(data.playPrompts[1].kind, "text"); + assert.equal(data.playPrompts[2].kind, "ssml"); + assert.equal(request.method, "POST"); + }); + + it("makes successful CancelAllMediaOperations request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + await callMedia.cancelAllOperations(); + + const request = spy.getCall(0).args[0]; + + assert.equal(request.method, "POST"); + }); + + it("makes successful StartContinuousDtmfRecognition request", async function () { + const mockHttpClient = generateHttpClient(200); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const continuousDtmfRecognitionOptions: ContinuousDtmfRecognitionOptions = { + operationContext: "test_operation_context", + }; + + await callMedia.startContinuousDtmfRecognition( + targetParticipant, + continuousDtmfRecognitionOptions, + ); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.deepEqual(data.targetParticipant, serializeCommunicationIdentifier(targetParticipant)); + assert.equal(data.operationContext, continuousDtmfRecognitionOptions.operationContext); + assert.equal(request.method, "POST"); + }); + + it("makes successful StopContinuousDtmfRecognition request", async function () { + const mockHttpClient = generateHttpClient(200); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const continuousDtmfRecognitionOptions: ContinuousDtmfRecognitionOptions = { + operationContext: "test_operation_context", + }; + + await callMedia.stopContinuousDtmfRecognition( + targetParticipant, + continuousDtmfRecognitionOptions, + ); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.deepEqual(data.targetParticipant, serializeCommunicationIdentifier(targetParticipant)); + assert.equal(data.operationContext, continuousDtmfRecognitionOptions.operationContext); + assert.equal(request.method, "POST"); + }); + + it("makes successful SendDtmf request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const targetParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const sendDtmfOptions: SendDtmfTonesOptions = { + operationContext: "test_operation_context", + }; + const tones = ["one", "two", "three", "pound"]; + + await callMedia.sendDtmfTones(tones, targetParticipant, sendDtmfOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.deepEqual(data.targetParticipant, serializeCommunicationIdentifier(targetParticipant)); + assert.deepEqual(data.tones, tones); + assert.equal(data.operationContext, sendDtmfOptions.operationContext); + assert.equal(request.method, "POST"); + }); + + it("makes successful hold request", async function () { + const mockHttpClient = generateHttpClient(200); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const playSource: TextSource = { + text: "test test test", + customVoiceEndpointId: "customVoiceEndpointId", + kind: "textSource", + }; + + const participantToHold: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const options: HoldOptions = { + playSource: playSource, + operationContext: "withPlaySource", + operationCallbackUrl: "https://localhost", + }; + await callMedia.hold(participantToHold, options); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.equal(data.targetParticipant.rawId, CALL_TARGET_ID); + assert.equal(data.playSourceInfo.kind, "text"); + assert.equal(data.playSourceInfo.text.text, playSource.text); + assert.equal(data.operationContext, "withPlaySource"); + assert.equal(data.operationCallbackUri, "https://localhost"); + assert.equal(request.method, "POST"); + }); + + it("makes successful Hold request with no playSource", async function () { + const mockHttpClient = generateHttpClient(200); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const participantToHold: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + await callMedia.hold(participantToHold); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.equal(data.targetParticipant.rawId, CALL_TARGET_ID); + assert.equal(request.method, "POST"); + assert.isUndefined(data.playSourceInfo); + }); + + it("makes successful unhold request", async function () { + const mockHttpClient = generateHttpClient(200); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const participantToUnhold: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const options: UnholdOptions = { + operationContext: "unholdContext", + }; + await callMedia.unhold(participantToUnhold, options); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.equal(data.targetParticipant.rawId, CALL_TARGET_ID); + assert.equal(request.method, "POST"); + assert.equal(data.operationContext, options.operationContext); + }); + + it("makes successful Start Transcription request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const startTranscriptionOptions: StartTranscriptionOptions = { + locale: "en-US", + operationContext: "test_operation_context", + }; + + await callMedia.startTranscription(startTranscriptionOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.locale, startTranscriptionOptions.locale); + assert.equal(data.operationContext, startTranscriptionOptions.operationContext); + assert.equal(request.method, "POST"); + }); + + it("makes successful Stop TranscriptionOptions request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const stopTranscriptionOptions: StopTranscriptionOptions = { + operationContext: "test_operation_context", + }; + + await callMedia.stopTranscription(stopTranscriptionOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.operationContext, stopTranscriptionOptions.operationContext); + assert.equal(request.method, "POST"); + }); + + it("makes successful Update Transcription request", async function () { + const mockHttpClient = generateHttpClient(202); + + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const locale = "en-US"; + + await callMedia.updateTranscription(locale); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.locale, locale); + assert.equal(request.method, "POST"); + }); + + it("makes successful start media streaming request with options", async function () { + const mockHttpClient = generateHttpClient(202); + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const options: StartMediaStreamingOptions = { + operationContext: "startMediaStreamContext", + operationCallbackUrl: "https://localhost", + }; + await callMedia.startMediaStreaming(options); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.equal(data.operationContext, options.operationContext); + assert.equal(data.operationCallbackUri, options.operationCallbackUrl); + assert.equal(request.method, "POST"); + }); + + it("makes successful start media streaming request without options", async function () { + const mockHttpClient = generateHttpClient(202); + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + await callMedia.startMediaStreaming(); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.isUndefined(data.operationContext); + assert.isUndefined(data.operationCallbackUri); + assert.equal(request.method, "POST"); + }); + + it("makes successful stop media streaming request with options", async function () { + const mockHttpClient = generateHttpClient(202); + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + const options: StopMediaStreamingOptions = { + operationCallbackUrl: "https://localhost", + }; + await callMedia.stopMediaStreaming(options); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.equal(data.operationCallbackUri, options.operationCallbackUrl); + assert.equal(request.method, "POST"); + }); + + it("makes successful stop media streaming request without options", async function () { + const mockHttpClient = generateHttpClient(202); + callMedia = createMediaClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + await callMedia.stopMediaStreaming(); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + assert.isUndefined(data.operationCallbackUri); + assert.equal(request.method, "POST"); + }); }); describe("Call Media Client Live Tests", function () { - let recorder: Recorder; - let callerCallAutomationClient: CallAutomationClient; - let receiverCallAutomationClient: CallAutomationClient; - let callConnection: CallConnection; - let testUser: CommunicationUserIdentifier; - let testUser2: CommunicationUserIdentifier; - let callerPhoneUser: PhoneNumberIdentifier; - let receiverPhoneUser: PhoneNumberIdentifier; - let testName: string; - - beforeEach(async function (this: Context) { - recorder = await createRecorder(this.currentTest); - testUser = await createTestUser(recorder); - testUser2 = await createTestUser(recorder); - callerCallAutomationClient = createCallAutomationClient(recorder, testUser); - receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + let recorder: Recorder; + let callerCallAutomationClient: CallAutomationClient; + let receiverCallAutomationClient: CallAutomationClient; + let callConnection: CallConnection; + let testUser: CommunicationUserIdentifier; + let testUser2: CommunicationUserIdentifier; + let callerPhoneUser: PhoneNumberIdentifier; + let receiverPhoneUser: PhoneNumberIdentifier; + let testName: string; + + beforeEach(async function (this: Context) { + recorder = await createRecorder(this.currentTest); + testUser = await createTestUser(recorder); + testUser2 = await createTestUser(recorder); + callerCallAutomationClient = createCallAutomationClient(recorder, testUser); + receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + }); + + afterEach(async function (this: Context) { + persistEvents(testName); + serviceBusReceivers.forEach((receiver) => { + receiver.close(); }); - - afterEach(async function (this: Context) { - persistEvents(testName); - serviceBusReceivers.forEach((receiver) => { - receiver.close(); - }); - events.forEach((callConnectionEvents) => { - callConnectionEvents.clear(); - }); - events.clear(); - serviceBusReceivers.clear(); - incomingCallContexts.clear(); - await recorder.stop(); - if (callConnection) { - try { - await callConnection.hangUp(true); - } catch { - return; - } - } + events.forEach((callConnectionEvents) => { + callConnectionEvents.clear(); }); - - it("Play audio to target participant", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_audio_to_target_participant"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "playAudioCreateCall" }; - - 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 answerCallOption: AnswerCallOptions = { operationContext: "playAudioAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playSource: FileSource[] = [ - { - url: fileSourceUrl, - kind: "fileSource", - }, - ]; - - const playOption: PlayOptions = { operationContext: "playAudio" }; - await callConnection.getCallMedia().play(playSource, [testUser2], playOption); - const playCompletedEvent = await waitForEvent("PlayCompleted", callConnectionId, 20000); - assert.isDefined(playCompletedEvent); - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Play audio to all participants", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_audio_to_all_participants"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { operationContext: "playToAllCreateCall" }; - - 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 answerCallOption: AnswerCallOptions = { operationContext: "playToAllAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playSource: FileSource[] = [ - { - url: fileSourceUrl, - kind: "fileSource", - }, - ]; - - const playOption: PlayOptions = { operationContext: "playToAllAudio" }; - await callConnection.getCallMedia().playToAll(playSource, playOption); - - const playCompletedEvent = await waitForEvent("PlayCompleted", callConnectionId, 20000); - assert.isDefined(playCompletedEvent); - + events.clear(); + serviceBusReceivers.clear(); + incomingCallContexts.clear(); + await recorder.stop(); + if (callConnection) { + try { await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Cancel all media operations", async function () { - testName = this.test?.fullTitle() - ? this.test?.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: "CancelMediaCreateCall" }; - - 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 answerCallOption: AnswerCallOptions = { operationContext: "CancelMediaAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playSource: FileSource[] = [ - { - url: fileSourceUrl, - kind: "fileSource", - }, - ]; - - const playOption: PlayOptions = { operationContext: "CancelplayToAllAudio" }; - await callConnection.getCallMedia().playToAll(playSource, playOption); - await callConnection.getCallMedia().cancelAllOperations(); - - const playCanceledEvent = await waitForEvent("PlayCanceled", callConnectionId, 20000); - assert.isDefined(playCanceledEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Trigger DTMF actions", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "create_call_and_trigger_dtmf_actions_then_hang_up"; - await loadPersistedEvents(testName); - - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - - const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const continuousDtmfRecognitionOptions1: ContinuousDtmfRecognitionOptions = { - operationContext: "ContinuousDtmfRecognitionStart", - }; - await callConnection - .getCallMedia() - .startContinuousDtmfRecognition(receiverPhoneUser, continuousDtmfRecognitionOptions1); - - const continuousDtmfRecognitionOptions2: ContinuousDtmfRecognitionOptions = { - operationContext: "ContinuousDtmfRecognitionSend", - }; - await callConnection - .getCallMedia() - .sendDtmfTones([DtmfTone.Pound], receiverPhoneUser, continuousDtmfRecognitionOptions2); - const sendDtmfCompleted = await waitForEvent("SendDtmfTonesCompleted", callConnectionId, 8000); - assert.isDefined(sendDtmfCompleted); - - const continuousDtmfRecognitionOptions3: ContinuousDtmfRecognitionOptions = { - operationContext: "ContinuousDtmfRecognitionStop", - }; - await callConnection - .getCallMedia() - .stopContinuousDtmfRecognition(receiverPhoneUser, continuousDtmfRecognitionOptions3); - const continuousDtmfRecognitionStopped = await waitForEvent( - "ContinuousDtmfRecognitionStopped", - callConnectionId, - 8000, - ); - assert.isDefined(continuousDtmfRecognitionStopped); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Creates a call, start media streaming, and hangs up.", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "create_call_start_media_streaming_and_hang_up"; - await loadPersistedEvents(testName); - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - - const mediaStreamingOptions: MediaStreamingOptions = { - transportUrl: transportUrl, - transportType: "websocket", - contentType: "audio", - audioChannelType: "mixed", - startMediaStreaming: false, - }; - - const createCallOptions: CreateCallOptions = { - mediaStreamingOptions: mediaStreamingOptions, - }; - - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOptions, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - await callConnection.getCallMedia().startMediaStreaming(); - const mediaStreamingStarted = await waitForEvent( - "MediaStreamingStarted", - callConnectionId, - 8000, - ); - assert.isDefined(mediaStreamingStarted); - - await callConnection.getCallMedia().stopMediaStreaming(); - const mediaStreamingStopped = await waitForEvent( - "MediaStreamingStopped", - callConnectionId, - 8000, - ); - assert.isDefined(mediaStreamingStopped); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Answers a call, start media streaming, and hangs up", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "answer_call_start_media_streaming_and_hang_up"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - - const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - const mediaStreamingOptions: MediaStreamingOptions = { - transportUrl: transportUrl, - transportType: "websocket", - contentType: "audio", - audioChannelType: "mixed", - startMediaStreaming: false, - }; - const answerCallOptions: AnswerCallOptions = { - mediaStreamingOptions: mediaStreamingOptions, - }; - - const answerCallResult = await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOptions, - ); - - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - - const answerCallConnection = answerCallResult.callConnection; - const answerCallConnectionId: string = answerCallResult.callConnectionProperties - .callConnectionId - ? answerCallResult.callConnectionProperties.callConnectionId - : ""; - await answerCallConnection.getCallMedia().startMediaStreaming(); - const mediaStreamingStarted = await waitForEvent( - "MediaStreamingStarted", - answerCallConnectionId, - 8000, - ); - assert.isDefined(mediaStreamingStarted); - - await answerCallConnection.getCallMedia().stopMediaStreaming(); - const mediaStreamingStopped = await waitForEvent( - "MediaStreamingStopped", - answerCallConnectionId, - 8000, - ); - assert.isDefined(mediaStreamingStopped); - - await answerCallConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent( - "CallDisconnected", - answerCallConnectionId, - 8000, - ); - assert.isDefined(callDisconnectedEvent); - } - }).timeout(60000); - - it("Play multiple file sources with play and playall", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_multiple_file_sources_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playMultipleFileSources: FileSource[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "fileSource", url: fileSourceUrl }, - { kind: "fileSource", url: fileSourceUrl }, - ]; - - // play to all - await callConnection - .getCallMedia() - .playToAll(playMultipleFileSources, { operationContext: "multipleFileSourceContext" }); - - const playCompletedEventToFileSources = await waitForEvent( - "PlayCompleted", - callConnectionId, - 20000, - ); - assert.isDefined(playCompletedEventToFileSources); - - // play to target - await callConnection.getCallMedia().play(playMultipleFileSources, [testUser2], { - operationContext: "multipleFileSourceToTargetContext", - }); - const playCompletedEventToTargetFileSources = await waitForEvent( - "PlayCompleted", - callConnectionId, - 20000, - ); - assert.isDefined(playCompletedEventToTargetFileSources); - }).timeout(60000); - - it("Play multiple text sources with playall", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_multiple_text_sources_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playMultipleTextSources: TextSource[] = [ - { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test three", voiceName: "en-US-NancyNeural" }, - ]; - - await callConnection - .getCallMedia() - .playToAll(playMultipleTextSources, { operationContext: "multipleTextSourceContext" }); - - const playCompletedEvent = await waitForEvent("PlayCompleted", callConnectionId, 20000); - assert.isDefined(playCompletedEvent); - }).timeout(60000); - - it("Play multiple text sources with play", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_multiple_text_sources_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - console.log(cognitiveServiceEndpoint); - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playMultipleTextSources: TextSource[] = [ - { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test three", voiceName: "en-US-NancyNeural" }, - ]; - - await callConnection.getCallMedia().play(playMultipleTextSources, [testUser2], { - operationContext: "multipleTextSourceToTargetContext", - }); - const playCompletedEventToTargetTextSources = await waitForEvent( - "PlayCompleted", - callConnectionId, - 20000, - ); - assert.isDefined(playCompletedEventToTargetTextSources); - }).timeout(60000); - - it("Play combined text and file sources with playall", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_combined_text_and_file_sources_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const multiplePlaySources: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - await callConnection - .getCallMedia() - .playToAll(multiplePlaySources, { operationContext: "multipleSourceContext" }); - - const playCompletedEventMultipleSource = await waitForEvent( - "PlayCompleted", - callConnectionId, - 20000, - ); - assert.isDefined(playCompletedEventMultipleSource); - }).timeout(60000); - - it("Play combined text and file sources with play", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_combined_text_and_file_sources_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const multiplePlaySources: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - await callConnection.getCallMedia().play(multiplePlaySources, [testUser2], { - operationContext: "multipleSourceToTargetContext", - }); - const playCompletedEventToTargetMultipleSource = await waitForEvent( - "PlayCompleted", - callConnectionId, - 20000, - ); - assert.isDefined(playCompletedEventToTargetMultipleSource); - }).timeout(60000); - - it("Play wrong source with play", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_wrong_source_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const filePrompt: FileSource = { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }; - - await callConnection - .getCallMedia() - .play([filePrompt], [testUser2], { operationContext: "playFailContext" }); - const playFailedEventWithTargetParticipant = await waitForEvent( - "PlayFailed", - callConnectionId, - 20000, - ); - assert.isDefined(playFailedEventWithTargetParticipant); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Play wrong source with playall", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "play_wrong_source_with_play_and_playall"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - operationContext: "playMultipleSourcesCreateCall", - }; - - 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 answerCallOption: AnswerCallOptions = { - operationContext: "playMultipleSourcesAnswerCall", - }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const filePrompt: FileSource = { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }; - - await callConnection - .getCallMedia() - .playToAll([filePrompt], { operationContext: "playFailContext" }); - const playFailedEvent = await waitForEvent("PlayFailed", callConnectionId, 20000); - assert.isDefined(playFailedEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it.skip("DTMF recognize with multiple play sources test", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "dtmf_recognize_with_multiple_play_source"; - await loadPersistedEvents(testName); - - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playMultipleTextSources: TextSource[] = [ - { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, - ]; - const recognizeDtmfOptionsToTextSource: CallMediaRecognizeDtmfOptions = { - maxTonesToCollect: 1, - initialSilenceTimeoutInSeconds: 5, - playPrompts: playMultipleTextSources, - interToneTimeoutInSeconds: 5, - interruptPrompt: true, - stopDtmfTones: [DtmfTone.Pound], - kind: "callMediaRecognizeDtmfOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeDtmfOptionsToTextSource); - const recognizeFailedEventToTextSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToTextSource); - - const multiplePlaySources: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeDtmfOptionsToMultipleSource: CallMediaRecognizeDtmfOptions = { - maxTonesToCollect: 1, - initialSilenceTimeoutInSeconds: 5, - playPrompts: multiplePlaySources, - interToneTimeoutInSeconds: 5, - interruptPrompt: true, - stopDtmfTones: [DtmfTone.Pound], - kind: "callMediaRecognizeDtmfOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeDtmfOptionsToMultipleSource); - const recognizeFailedEventToMultipleSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToMultipleSource); - - const multiplePrompts: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeDtmfOptionsToMultiplePrompts: CallMediaRecognizeDtmfOptions = { - maxTonesToCollect: 1, - initialSilenceTimeoutInSeconds: 5, - playPrompts: multiplePrompts, - interToneTimeoutInSeconds: 5, - interruptPrompt: true, - stopDtmfTones: [DtmfTone.Pound], - kind: "callMediaRecognizeDtmfOptions", - }; - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeDtmfOptionsToMultiplePrompts); - const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); - assert.isDefined(recognizeFailedEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it.skip("Speech recognize with multiple play sources test", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "speech_recognize_with_multiple_play_source"; - await loadPersistedEvents(testName); - - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playMultipleTextSources: TextSource[] = [ - { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeSpeechOptionsToTextSource: CallMediaRecognizeSpeechOptions = { - endSilenceTimeoutInSeconds: 1, - playPrompts: playMultipleTextSources, - kind: "callMediaRecognizeSpeechOptions", - initialSilenceTimeoutInSeconds: 5, - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeSpeechOptionsToTextSource); - const recognizeFailedEventToTextSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToTextSource); - - const multiplePlaySources: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeSpeechOptionsMultipleSource: CallMediaRecognizeSpeechOptions = { - endSilenceTimeoutInSeconds: 1, - playPrompts: multiplePlaySources, - kind: "callMediaRecognizeSpeechOptions", - initialSilenceTimeoutInSeconds: 5, - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeSpeechOptionsMultipleSource); - const recognizeFailedEventToMultipleSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToMultipleSource); - - const multiplePrompts: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeSpeechOptionsMultiPrompts: CallMediaRecognizeSpeechOptions = { - endSilenceTimeoutInSeconds: 1, - playPrompts: multiplePrompts, - kind: "callMediaRecognizeSpeechOptions", - initialSilenceTimeoutInSeconds: 5, - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeSpeechOptionsMultiPrompts); - const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); - assert.isDefined(recognizeFailedEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it.skip("Choice recognize with multiple play sources test", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "choice_recognize_with_multiple_play_source"; - await loadPersistedEvents(testName); - - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const choices = [ - { - label: "Confirm", - phrases: ["Confirm", "First", "One"], - tone: DtmfTone.One, - }, - { - label: "Cancel", - phrases: ["Cancel", "Second", "Two"], - tone: DtmfTone.Two, - }, - ]; - - const playMultipleTextSources: TextSource[] = [ - { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeChoiceOptionsToTextSource: CallMediaRecognizeChoiceOptions = { - choices: choices, - interruptPrompt: true, - initialSilenceTimeoutInSeconds: 5, - playPrompts: playMultipleTextSources, - kind: "callMediaRecognizeChoiceOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeChoiceOptionsToTextSource); - const recognizeFailedEventToTextSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToTextSource); - - const multiplePlaySources: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeChoiceOptionsToMultipleSource: CallMediaRecognizeChoiceOptions = { - choices: choices, - interruptPrompt: true, - initialSilenceTimeoutInSeconds: 10, - playPrompts: multiplePlaySources, - kind: "callMediaRecognizeChoiceOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeChoiceOptionsToMultipleSource); - const recognizeFailedEventToMultipleSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToMultipleSource); - - const multiplePrompts: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeChoiceOptionsMultiplePrompts: CallMediaRecognizeChoiceOptions = { - choices: choices, - interruptPrompt: true, - initialSilenceTimeoutInSeconds: 5, - playPrompts: multiplePrompts, - kind: "callMediaRecognizeChoiceOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeChoiceOptionsMultiplePrompts); - const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); - assert.isDefined(recognizeFailedEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it.skip("SpeechOrDtmf recognize with multiple play sources test", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "speechOrDtmf_recognize_with_multiple_play_source"; - await loadPersistedEvents(testName); - - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - const createCallOption: CreateCallOptions = { - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - createCallOption, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playMultipleTextSources: TextSource[] = [ - { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, - { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeSpeechOrDtmfOptionsTextSource: CallMediaRecognizeSpeechOrDtmfOptions = { - maxTonesToCollect: 1, - endSilenceTimeoutInSeconds: 1, - playPrompts: playMultipleTextSources, - initialSilenceTimeoutInSeconds: 5, - interruptPrompt: true, - kind: "callMediaRecognizeSpeechOrDtmfOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeSpeechOrDtmfOptionsTextSource); - console.log("callMediaRecognizeSpeechOrDtmfOptions"); - const recognizeFailedEventToTextSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToTextSource); - - const multiplePlaySources: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: fileSourceUrl }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeSpeechOrDtmfOptionsMultipleSource: CallMediaRecognizeSpeechOrDtmfOptions = { - maxTonesToCollect: 1, - endSilenceTimeoutInSeconds: 1, - playPrompts: multiplePlaySources, - initialSilenceTimeoutInSeconds: 5, - interruptPrompt: true, - kind: "callMediaRecognizeSpeechOrDtmfOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeSpeechOrDtmfOptionsMultipleSource); - const recognizeFailedEventToMultipleSource = await waitForEvent( - "RecognizeFailed", - callConnectionId, - 8000, - ); - assert.isDefined(recognizeFailedEventToMultipleSource); - - const multiplePrompts: (FileSource | TextSource)[] = [ - { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, - { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, - ]; - - const recognizeSpeechOrDtmfOptionsMultiplePropmt: CallMediaRecognizeSpeechOrDtmfOptions = { - maxTonesToCollect: 1, - endSilenceTimeoutInSeconds: 1, - playPrompts: multiplePrompts, - initialSilenceTimeoutInSeconds: 5, - interruptPrompt: true, - kind: "callMediaRecognizeSpeechOrDtmfOptions", - }; - - await callConnection - .getCallMedia() - .startRecognizing(receiverPhoneUser, recognizeSpeechOrDtmfOptionsMultiplePropmt); - - const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); - assert.isDefined(recognizeFailedEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Hold Unhold participant in a call", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "hold_unhold_participant_in_a_call"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - - const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - await callConnection.getCallMedia().hold(testUser2); - await ((ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)))(3000); - const participantHold: CallParticipant = await callConnection.getParticipant(testUser2); - assert.isDefined(participantHold); - assert.isTrue(participantHold.isOnHold); - - await callConnection.getCallMedia().unhold(testUser2); - await ((ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)))(3000); - const participantUnhold = await callConnection.getParticipant(testUser2); - assert.isDefined(participantUnhold); - assert.isFalse(participantUnhold.isOnHold); - - const playSource: FileSource = { - url: "https://dummy.com/dummyurl.wav", - kind: "fileSource", - }; - - const holdOptions: HoldOptions = { - playSource: playSource, - operationContext: "holdFailedContext", - }; - await callConnection.getCallMedia().hold(testUser2, holdOptions); - const holdFailedEvent = await waitForEvent("HoldFailed", callConnectionId, 8000); - assert.isDefined(holdFailedEvent); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Creates a call, start transcription, and hangs up.", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "create_call_start_transcription_and_hang_up"; - await loadPersistedEvents(testName); - const phoneNumbers = await getPhoneNumbers(recorder); - assert.isAtLeast( - phoneNumbers.length, - 2, - "Invalid PSTN setup, test needs at least 2 phone numbers", - ); - callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; - - const callInvite: CallInvite = { - targetParticipant: receiverPhoneUser, - sourceCallIdNumber: callerPhoneUser, - }; - const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - - const transcriptionOptions: TranscriptionOptions = { - transportUrl: transportUrl, - transportType: "websocket", - locale: "en-US", - startTranscription: false, - enableIntermediateResults: false, - }; - - const creatCallOptions: CreateCallOptions = { - transcriptionOptions: transcriptionOptions, - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - - const result = await callerCallAutomationClient.createCall( - callInvite, - callBackUrl, - creatCallOptions, - ); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - await callConnection.getCallMedia().startTranscription(); - const transcriptionStarted = await waitForEvent("TranscriptionStarted", callConnectionId, 8000); - assert.isDefined(transcriptionStarted); - - await callConnection.getCallMedia().stopTranscription(); - const transcriptionStopped = waitForEvent("TranscriptionStopped", callConnectionId, 8000); - assert.isDefined(transcriptionStopped); - - await callConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); - assert.isDefined(callDisconnectedEvent); - }).timeout(60000); - - it("Answers a call, start transcription, and hangs up", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "answer_call_start_transcription_and_hang_up"; - await loadPersistedEvents(testName); - - const callInvite: CallInvite = { targetParticipant: testUser2 }; - const uniqueId = await serviceBusWithNewCall(testUser, testUser2); - const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; - - const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); - const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); - const callConnectionId: string = result.callConnectionProperties.callConnectionId - ? result.callConnectionProperties.callConnectionId - : ""; - assert.isDefined(incomingCallContext); - - if (incomingCallContext) { - const transcriptionOptions: TranscriptionOptions = { - transportUrl: transportUrl, - transportType: "websocket", - locale: "en-US", - startTranscription: false, - enableIntermediateResults: false, - }; - const answerCallOptions: AnswerCallOptions = { - transcriptionOptions: transcriptionOptions, - callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, - }; - const answerCallResult = await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOptions, - ); - - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - assert.isDefined(callConnectedEvent); - - const answerCallConnection = answerCallResult.callConnection; - const answerCallConnectionId: string = answerCallResult.callConnectionProperties - .callConnectionId - ? answerCallResult.callConnectionProperties.callConnectionId - : ""; - - await answerCallConnection.getCallMedia().startTranscription(); - const transcriptionStarted = await waitForEvent( - "TranscriptionStarted", - answerCallConnectionId, - 8000, - ); - assert.isDefined(transcriptionStarted); - - await answerCallConnection.getCallMedia().stopTranscription(); - const transcriptionStopped = waitForEvent( - "TranscriptionStopped", - answerCallConnectionId, - 8000, - ); - assert.isDefined(transcriptionStopped); - - await answerCallConnection.hangUp(true); - const callDisconnectedEvent = await waitForEvent( - "CallDisconnected", - answerCallConnectionId, - 8000, - ); - assert.isDefined(callDisconnectedEvent); - } - }).timeout(60000); + } catch { + return; + } + } + }); + + it("Play audio to target participant", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_audio_to_target_participant"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "playAudioCreateCall" }; + + 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 answerCallOption: AnswerCallOptions = { operationContext: "playAudioAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playSource: FileSource[] = [ + { + url: fileSourceUrl, + kind: "fileSource", + }, + ]; + + const playOption: PlayOptions = { operationContext: "playAudio" }; + await callConnection.getCallMedia().play(playSource, [testUser2], playOption); + const playCompletedEvent = await waitForEvent("PlayCompleted", callConnectionId, 20000); + assert.isDefined(playCompletedEvent); + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Play audio to all participants", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_audio_to_all_participants"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { operationContext: "playToAllCreateCall" }; + + 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 answerCallOption: AnswerCallOptions = { operationContext: "playToAllAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playSource: FileSource[] = [ + { + url: fileSourceUrl, + kind: "fileSource", + }, + ]; + + const playOption: PlayOptions = { operationContext: "playToAllAudio" }; + await callConnection.getCallMedia().playToAll(playSource, playOption); + + const playCompletedEvent = await waitForEvent("PlayCompleted", callConnectionId, 20000); + assert.isDefined(playCompletedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Cancel all media operations", async function () { + testName = this.test?.fullTitle() + ? this.test?.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: "CancelMediaCreateCall" }; + + 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 answerCallOption: AnswerCallOptions = { operationContext: "CancelMediaAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playSource: FileSource[] = [ + { + url: fileSourceUrl, + kind: "fileSource", + }, + ]; + + const playOption: PlayOptions = { operationContext: "CancelplayToAllAudio" }; + await callConnection.getCallMedia().playToAll(playSource, playOption); + await callConnection.getCallMedia().cancelAllOperations(); + + const playCanceledEvent = await waitForEvent("PlayCanceled", callConnectionId, 20000); + assert.isDefined(playCanceledEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Trigger DTMF actions", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "create_call_and_trigger_dtmf_actions_then_hang_up"; + await loadPersistedEvents(testName); + + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + + const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const continuousDtmfRecognitionOptions1: ContinuousDtmfRecognitionOptions = { + operationContext: "ContinuousDtmfRecognitionStart", + }; + await callConnection + .getCallMedia() + .startContinuousDtmfRecognition(receiverPhoneUser, continuousDtmfRecognitionOptions1); + + const continuousDtmfRecognitionOptions2: ContinuousDtmfRecognitionOptions = { + operationContext: "ContinuousDtmfRecognitionSend", + }; + await callConnection + .getCallMedia() + .sendDtmfTones([DtmfTone.Pound], receiverPhoneUser, continuousDtmfRecognitionOptions2); + const sendDtmfCompleted = await waitForEvent("SendDtmfTonesCompleted", callConnectionId, 8000); + assert.isDefined(sendDtmfCompleted); + + const continuousDtmfRecognitionOptions3: ContinuousDtmfRecognitionOptions = { + operationContext: "ContinuousDtmfRecognitionStop", + }; + await callConnection + .getCallMedia() + .stopContinuousDtmfRecognition(receiverPhoneUser, continuousDtmfRecognitionOptions3); + const continuousDtmfRecognitionStopped = await waitForEvent( + "ContinuousDtmfRecognitionStopped", + callConnectionId, + 8000, + ); + assert.isDefined(continuousDtmfRecognitionStopped); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Creates a call, start media streaming, and hangs up.", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "create_call_start_media_streaming_and_hang_up"; + await loadPersistedEvents(testName); + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + + const mediaStreamingOptions: MediaStreamingOptions = { + transportUrl: transportUrl, + transportType: "websocket", + contentType: "audio", + audioChannelType: "mixed", + startMediaStreaming: false, + }; + + const createCallOptions: CreateCallOptions = { + mediaStreamingOptions: mediaStreamingOptions, + }; + + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOptions, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + await callConnection.getCallMedia().startMediaStreaming(); + const mediaStreamingStarted = await waitForEvent( + "MediaStreamingStarted", + callConnectionId, + 8000, + ); + assert.isDefined(mediaStreamingStarted); + + await callConnection.getCallMedia().stopMediaStreaming(); + const mediaStreamingStopped = await waitForEvent( + "MediaStreamingStopped", + callConnectionId, + 8000, + ); + assert.isDefined(mediaStreamingStopped); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Answers a call, start media streaming, and hangs up", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "answer_call_start_media_streaming_and_hang_up"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + + const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + const mediaStreamingOptions: MediaStreamingOptions = { + transportUrl: transportUrl, + transportType: "websocket", + contentType: "audio", + audioChannelType: "mixed", + startMediaStreaming: false, + }; + const answerCallOptions: AnswerCallOptions = { + mediaStreamingOptions: mediaStreamingOptions, + }; + + const answerCallResult = await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOptions, + ); + + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + + const answerCallConnection = answerCallResult.callConnection; + const answerCallConnectionId: string = answerCallResult.callConnectionProperties + .callConnectionId + ? answerCallResult.callConnectionProperties.callConnectionId + : ""; + await answerCallConnection.getCallMedia().startMediaStreaming(); + const mediaStreamingStarted = await waitForEvent( + "MediaStreamingStarted", + answerCallConnectionId, + 8000, + ); + assert.isDefined(mediaStreamingStarted); + + await answerCallConnection.getCallMedia().stopMediaStreaming(); + const mediaStreamingStopped = await waitForEvent( + "MediaStreamingStopped", + answerCallConnectionId, + 8000, + ); + assert.isDefined(mediaStreamingStopped); + + await answerCallConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent( + "CallDisconnected", + answerCallConnectionId, + 8000, + ); + assert.isDefined(callDisconnectedEvent); + } + }).timeout(60000); + + it("Play multiple file sources with play and playall", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_multiple_file_sources_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playMultipleFileSources: FileSource[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "fileSource", url: fileSourceUrl }, + { kind: "fileSource", url: fileSourceUrl }, + ]; + + // play to all + await callConnection + .getCallMedia() + .playToAll(playMultipleFileSources, { operationContext: "multipleFileSourceContext" }); + + const playCompletedEventToFileSources = await waitForEvent( + "PlayCompleted", + callConnectionId, + 20000, + ); + assert.isDefined(playCompletedEventToFileSources); + + // play to target + await callConnection.getCallMedia().play(playMultipleFileSources, [testUser2], { + operationContext: "multipleFileSourceToTargetContext", + }); + const playCompletedEventToTargetFileSources = await waitForEvent( + "PlayCompleted", + callConnectionId, + 20000, + ); + assert.isDefined(playCompletedEventToTargetFileSources); + }).timeout(60000); + + it("Play multiple text sources with playall", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_multiple_text_sources_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playMultipleTextSources: TextSource[] = [ + { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test three", voiceName: "en-US-NancyNeural" }, + ]; + + await callConnection + .getCallMedia() + .playToAll(playMultipleTextSources, { operationContext: "multipleTextSourceContext" }); + + const playCompletedEvent = await waitForEvent("PlayCompleted", callConnectionId, 20000); + assert.isDefined(playCompletedEvent); + }).timeout(60000); + + it("Play multiple text sources with play", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_multiple_text_sources_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + console.log(cognitiveServiceEndpoint); + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playMultipleTextSources: TextSource[] = [ + { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test three", voiceName: "en-US-NancyNeural" }, + ]; + + await callConnection.getCallMedia().play(playMultipleTextSources, [testUser2], { + operationContext: "multipleTextSourceToTargetContext", + }); + const playCompletedEventToTargetTextSources = await waitForEvent( + "PlayCompleted", + callConnectionId, + 20000, + ); + assert.isDefined(playCompletedEventToTargetTextSources); + }).timeout(60000); + + it("Play combined text and file sources with playall", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_combined_text_and_file_sources_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const multiplePlaySources: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + await callConnection + .getCallMedia() + .playToAll(multiplePlaySources, { operationContext: "multipleSourceContext" }); + + const playCompletedEventMultipleSource = await waitForEvent( + "PlayCompleted", + callConnectionId, + 20000, + ); + assert.isDefined(playCompletedEventMultipleSource); + }).timeout(60000); + + it("Play combined text and file sources with play", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_combined_text_and_file_sources_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const multiplePlaySources: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + await callConnection.getCallMedia().play(multiplePlaySources, [testUser2], { + operationContext: "multipleSourceToTargetContext", + }); + const playCompletedEventToTargetMultipleSource = await waitForEvent( + "PlayCompleted", + callConnectionId, + 20000, + ); + assert.isDefined(playCompletedEventToTargetMultipleSource); + }).timeout(60000); + + it("Play wrong source with play", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_wrong_source_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const filePrompt: FileSource = { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }; + + await callConnection + .getCallMedia() + .play([filePrompt], [testUser2], { operationContext: "playFailContext" }); + const playFailedEventWithTargetParticipant = await waitForEvent( + "PlayFailed", + callConnectionId, + 20000, + ); + assert.isDefined(playFailedEventWithTargetParticipant); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Play wrong source with playall", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "play_wrong_source_with_play_and_playall"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + operationContext: "playMultipleSourcesCreateCall", + }; + + 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 answerCallOption: AnswerCallOptions = { + operationContext: "playMultipleSourcesAnswerCall", + }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const filePrompt: FileSource = { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }; + + await callConnection + .getCallMedia() + .playToAll([filePrompt], { operationContext: "playFailContext" }); + const playFailedEvent = await waitForEvent("PlayFailed", callConnectionId, 20000); + assert.isDefined(playFailedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it.skip("DTMF recognize with multiple play sources test", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "dtmf_recognize_with_multiple_play_source"; + await loadPersistedEvents(testName); + + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playMultipleTextSources: TextSource[] = [ + { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, + ]; + const recognizeDtmfOptionsToTextSource: CallMediaRecognizeDtmfOptions = { + maxTonesToCollect: 1, + initialSilenceTimeoutInSeconds: 5, + playPrompts: playMultipleTextSources, + interToneTimeoutInSeconds: 5, + interruptPrompt: true, + stopDtmfTones: [DtmfTone.Pound], + kind: "callMediaRecognizeDtmfOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeDtmfOptionsToTextSource); + const recognizeFailedEventToTextSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToTextSource); + + const multiplePlaySources: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeDtmfOptionsToMultipleSource: CallMediaRecognizeDtmfOptions = { + maxTonesToCollect: 1, + initialSilenceTimeoutInSeconds: 5, + playPrompts: multiplePlaySources, + interToneTimeoutInSeconds: 5, + interruptPrompt: true, + stopDtmfTones: [DtmfTone.Pound], + kind: "callMediaRecognizeDtmfOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeDtmfOptionsToMultipleSource); + const recognizeFailedEventToMultipleSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToMultipleSource); + + const multiplePrompts: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeDtmfOptionsToMultiplePrompts: CallMediaRecognizeDtmfOptions = { + maxTonesToCollect: 1, + initialSilenceTimeoutInSeconds: 5, + playPrompts: multiplePrompts, + interToneTimeoutInSeconds: 5, + interruptPrompt: true, + stopDtmfTones: [DtmfTone.Pound], + kind: "callMediaRecognizeDtmfOptions", + }; + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeDtmfOptionsToMultiplePrompts); + const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); + assert.isDefined(recognizeFailedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it.skip("Speech recognize with multiple play sources test", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "speech_recognize_with_multiple_play_source"; + await loadPersistedEvents(testName); + + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playMultipleTextSources: TextSource[] = [ + { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeSpeechOptionsToTextSource: CallMediaRecognizeSpeechOptions = { + endSilenceTimeoutInSeconds: 1, + playPrompts: playMultipleTextSources, + kind: "callMediaRecognizeSpeechOptions", + initialSilenceTimeoutInSeconds: 5, + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeSpeechOptionsToTextSource); + const recognizeFailedEventToTextSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToTextSource); + + const multiplePlaySources: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeSpeechOptionsMultipleSource: CallMediaRecognizeSpeechOptions = { + endSilenceTimeoutInSeconds: 1, + playPrompts: multiplePlaySources, + kind: "callMediaRecognizeSpeechOptions", + initialSilenceTimeoutInSeconds: 5, + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeSpeechOptionsMultipleSource); + const recognizeFailedEventToMultipleSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToMultipleSource); + + const multiplePrompts: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeSpeechOptionsMultiPrompts: CallMediaRecognizeSpeechOptions = { + endSilenceTimeoutInSeconds: 1, + playPrompts: multiplePrompts, + kind: "callMediaRecognizeSpeechOptions", + initialSilenceTimeoutInSeconds: 5, + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeSpeechOptionsMultiPrompts); + const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); + assert.isDefined(recognizeFailedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it.skip("Choice recognize with multiple play sources test", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "choice_recognize_with_multiple_play_source"; + await loadPersistedEvents(testName); + + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const choices = [ + { + label: "Confirm", + phrases: ["Confirm", "First", "One"], + tone: DtmfTone.One, + }, + { + label: "Cancel", + phrases: ["Cancel", "Second", "Two"], + tone: DtmfTone.Two, + }, + ]; + + const playMultipleTextSources: TextSource[] = [ + { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeChoiceOptionsToTextSource: CallMediaRecognizeChoiceOptions = { + choices: choices, + interruptPrompt: true, + initialSilenceTimeoutInSeconds: 5, + playPrompts: playMultipleTextSources, + kind: "callMediaRecognizeChoiceOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeChoiceOptionsToTextSource); + const recognizeFailedEventToTextSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToTextSource); + + const multiplePlaySources: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeChoiceOptionsToMultipleSource: CallMediaRecognizeChoiceOptions = { + choices: choices, + interruptPrompt: true, + initialSilenceTimeoutInSeconds: 10, + playPrompts: multiplePlaySources, + kind: "callMediaRecognizeChoiceOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeChoiceOptionsToMultipleSource); + const recognizeFailedEventToMultipleSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToMultipleSource); + + const multiplePrompts: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeChoiceOptionsMultiplePrompts: CallMediaRecognizeChoiceOptions = { + choices: choices, + interruptPrompt: true, + initialSilenceTimeoutInSeconds: 5, + playPrompts: multiplePrompts, + kind: "callMediaRecognizeChoiceOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeChoiceOptionsMultiplePrompts); + const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); + assert.isDefined(recognizeFailedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it.skip("SpeechOrDtmf recognize with multiple play sources test", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "speechOrDtmf_recognize_with_multiple_play_source"; + await loadPersistedEvents(testName); + + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + const createCallOption: CreateCallOptions = { + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + createCallOption, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playMultipleTextSources: TextSource[] = [ + { kind: "textSource", text: "this is test one", voiceName: "en-US-NancyNeural" }, + { kind: "textSource", text: "this is test two", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeSpeechOrDtmfOptionsTextSource: CallMediaRecognizeSpeechOrDtmfOptions = { + maxTonesToCollect: 1, + endSilenceTimeoutInSeconds: 1, + playPrompts: playMultipleTextSources, + initialSilenceTimeoutInSeconds: 5, + interruptPrompt: true, + kind: "callMediaRecognizeSpeechOrDtmfOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeSpeechOrDtmfOptionsTextSource); + console.log("callMediaRecognizeSpeechOrDtmfOptions"); + const recognizeFailedEventToTextSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToTextSource); + + const multiplePlaySources: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: fileSourceUrl }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeSpeechOrDtmfOptionsMultipleSource: CallMediaRecognizeSpeechOrDtmfOptions = { + maxTonesToCollect: 1, + endSilenceTimeoutInSeconds: 1, + playPrompts: multiplePlaySources, + initialSilenceTimeoutInSeconds: 5, + interruptPrompt: true, + kind: "callMediaRecognizeSpeechOrDtmfOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeSpeechOrDtmfOptionsMultipleSource); + const recognizeFailedEventToMultipleSource = await waitForEvent( + "RecognizeFailed", + callConnectionId, + 8000, + ); + assert.isDefined(recognizeFailedEventToMultipleSource); + + const multiplePrompts: (FileSource | TextSource)[] = [ + { kind: "fileSource", url: "https://dummy.com/dummyurl.wav" }, + { kind: "textSource", text: "this is test", voiceName: "en-US-NancyNeural" }, + ]; + + const recognizeSpeechOrDtmfOptionsMultiplePropmt: CallMediaRecognizeSpeechOrDtmfOptions = { + maxTonesToCollect: 1, + endSilenceTimeoutInSeconds: 1, + playPrompts: multiplePrompts, + initialSilenceTimeoutInSeconds: 5, + interruptPrompt: true, + kind: "callMediaRecognizeSpeechOrDtmfOptions", + }; + + await callConnection + .getCallMedia() + .startRecognizing(receiverPhoneUser, recognizeSpeechOrDtmfOptionsMultiplePropmt); + + const recognizeFailedEvent = await waitForEvent("RecognizeFailed", callConnectionId, 8000); + assert.isDefined(recognizeFailedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Hold Unhold participant in a call", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "hold_unhold_participant_in_a_call"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + + const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + await callConnection.getCallMedia().hold(testUser2); + await ((ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)))(3000); + const participantHold: CallParticipant = await callConnection.getParticipant(testUser2); + assert.isDefined(participantHold); + assert.isTrue(participantHold.isOnHold); + + await callConnection.getCallMedia().unhold(testUser2); + await ((ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)))(3000); + const participantUnhold = await callConnection.getParticipant(testUser2); + assert.isDefined(participantUnhold); + assert.isFalse(participantUnhold.isOnHold); + + const playSource: FileSource = { + url: "https://dummy.com/dummyurl.wav", + kind: "fileSource", + }; + + const holdOptions: HoldOptions = { + playSource: playSource, + operationContext: "holdFailedContext", + }; + await callConnection.getCallMedia().hold(testUser2, holdOptions); + const holdFailedEvent = await waitForEvent("HoldFailed", callConnectionId, 8000); + assert.isDefined(holdFailedEvent); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Creates a call, start transcription, and hangs up.", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "create_call_start_transcription_and_hang_up"; + await loadPersistedEvents(testName); + const phoneNumbers = await getPhoneNumbers(recorder); + assert.isAtLeast( + phoneNumbers.length, + 2, + "Invalid PSTN setup, test needs at least 2 phone numbers", + ); + callerPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + receiverPhoneUser = { phoneNumber: phoneNumbers.pop() as string }; + + const callInvite: CallInvite = { + targetParticipant: receiverPhoneUser, + sourceCallIdNumber: callerPhoneUser, + }; + const uniqueId = await serviceBusWithNewCall(callerPhoneUser, receiverPhoneUser); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + + const transcriptionOptions: TranscriptionOptions = { + transportUrl: transportUrl, + transportType: "websocket", + locale: "en-US", + startTranscription: false, + enableIntermediateResults: false, + }; + + const creatCallOptions: CreateCallOptions = { + transcriptionOptions: transcriptionOptions, + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + + const result = await callerCallAutomationClient.createCall( + callInvite, + callBackUrl, + creatCallOptions, + ); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 30000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + await receiverCallAutomationClient.answerCall(incomingCallContext, callBackUrl); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + await callConnection.getCallMedia().startTranscription(); + const transcriptionStarted = await waitForEvent("TranscriptionStarted", callConnectionId, 8000); + assert.isDefined(transcriptionStarted); + + await callConnection.getCallMedia().stopTranscription(); + const transcriptionStopped = waitForEvent("TranscriptionStopped", callConnectionId, 8000); + assert.isDefined(transcriptionStopped); + + await callConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent("CallDisconnected", callConnectionId, 8000); + assert.isDefined(callDisconnectedEvent); + }).timeout(60000); + + it("Answers a call, start transcription, and hangs up", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "answer_call_start_transcription_and_hang_up"; + await loadPersistedEvents(testName); + + const callInvite: CallInvite = { targetParticipant: testUser2 }; + const uniqueId = await serviceBusWithNewCall(testUser, testUser2); + const callBackUrl: string = dispatcherCallback + `?q=${uniqueId}`; + + const result = await callerCallAutomationClient.createCall(callInvite, callBackUrl); + const incomingCallContext = await waitForIncomingCallContext(uniqueId, 8000); + const callConnectionId: string = result.callConnectionProperties.callConnectionId + ? result.callConnectionProperties.callConnectionId + : ""; + assert.isDefined(incomingCallContext); + + if (incomingCallContext) { + const transcriptionOptions: TranscriptionOptions = { + transportUrl: transportUrl, + transportType: "websocket", + locale: "en-US", + startTranscription: false, + enableIntermediateResults: false, + }; + const answerCallOptions: AnswerCallOptions = { + transcriptionOptions: transcriptionOptions, + callIntelligenceOptions: { cognitiveServicesEndpoint: cognitiveServiceEndpoint }, + }; + const answerCallResult = await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOptions, + ); + + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + assert.isDefined(callConnectedEvent); + + const answerCallConnection = answerCallResult.callConnection; + const answerCallConnectionId: string = answerCallResult.callConnectionProperties + .callConnectionId + ? answerCallResult.callConnectionProperties.callConnectionId + : ""; + + await answerCallConnection.getCallMedia().startTranscription(); + const transcriptionStarted = await waitForEvent( + "TranscriptionStarted", + answerCallConnectionId, + 8000, + ); + assert.isDefined(transcriptionStarted); + + await answerCallConnection.getCallMedia().stopTranscription(); + const transcriptionStopped = waitForEvent( + "TranscriptionStopped", + answerCallConnectionId, + 8000, + ); + assert.isDefined(transcriptionStopped); + + await answerCallConnection.hangUp(true); + const callDisconnectedEvent = await waitForEvent( + "CallDisconnected", + answerCallConnectionId, + 8000, + ); + assert.isDefined(callDisconnectedEvent); + } + }).timeout(60000); }); diff --git a/sdk/communication/communication-call-automation/test/callRecordingClient.spec.ts b/sdk/communication/communication-call-automation/test/callRecordingClient.spec.ts index ba1a1569c519..22ed4750fa60 100644 --- a/sdk/communication/communication-call-automation/test/callRecordingClient.spec.ts +++ b/sdk/communication/communication-call-automation/test/callRecordingClient.spec.ts @@ -6,20 +6,20 @@ import { assert } from "chai"; import * as RestModel from "../src/generated/src/models"; import { createRecordingClient, generateHttpClient } from "./utils/mockClient"; import { - baseUri, - CALL_CALLBACK_URL, - CALL_SERVER_CALL_ID, - CALL_TARGET_ID, - generateToken, - RECORDING_ID, - RECORDING_STATE, + baseUri, + CALL_CALLBACK_URL, + CALL_SERVER_CALL_ID, + CALL_TARGET_ID, + generateToken, + RECORDING_ID, + RECORDING_STATE, } from "./utils/connectionUtils"; import { CallRecording } from "../src/callRecording"; import { - AnswerCallOptions, - CreateCallOptions, - PlayOptions, - StartRecordingOptions, + AnswerCallOptions, + CreateCallOptions, + PlayOptions, + StartRecordingOptions, } from "../src/models/options"; import { ChannelAffinity } from "@azure/communication-call-automation"; import { CommunicationIdentifier, CommunicationUserIdentifier } from "@azure/communication-common"; @@ -27,247 +27,247 @@ import { CallAutomationClient, CallInvite, CallConnection } from "../src"; import { Recorder } from "@azure-tools/test-recorder"; import { Context } from "mocha"; import { - createRecorder, - createTestUser, - dispatcherCallback, - serviceBusWithNewCall, - createCallAutomationClient, - waitForIncomingCallContext, - waitForEvent, - events, - serviceBusReceivers, - incomingCallContexts, - loadPersistedEvents, - persistEvents, - fileSourceUrl, + createRecorder, + createTestUser, + dispatcherCallback, + serviceBusWithNewCall, + createCallAutomationClient, + waitForIncomingCallContext, + waitForEvent, + events, + serviceBusReceivers, + incomingCallContexts, + loadPersistedEvents, + persistEvents, + fileSourceUrl, } from "./utils/recordedClient"; import { FileSource } from "../src/models/models"; describe("CallRecording Unit Tests", async function () { - let callRecording: CallRecording; - - afterEach(function () { - sinon.restore(); - }); - - it("can instantiate", async function () { - new CallRecording(baseUri, { key: generateToken() }); - }); - - it("makes successful startRecording request with channel affinity", async function () { - const mockResponse: RestModel.RecordingStateResponse = { - recordingId: RECORDING_ID, - recordingState: RECORDING_STATE, - }; - - const mockHttpClient = generateHttpClient(200, mockResponse); - callRecording = createRecordingClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const channelZeroParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; - const channelAffinity: ChannelAffinity = { - targetParticipant: channelZeroParticipant, - channel: 0, - }; - - const recOptions: StartRecordingOptions = { - recordingStateCallbackEndpointUrl: CALL_CALLBACK_URL, - callLocator: { id: CALL_SERVER_CALL_ID, kind: "serverCallLocator" }, - recordingChannel: "unmixed", - recordingFormat: "wav", - recordingContent: "audio", - channelAffinity: [channelAffinity], - }; - - await callRecording.start(recOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.callLocator.kind, "serverCallLocator"); - assert.equal(data.channelAffinity[0].channel, 0); - assert.equal(data.channelAffinity[0].participant.rawId, CALL_TARGET_ID); - assert.equal(data.channelAffinity[0].participant.kind, "communicationUser"); - assert.equal(data.channelAffinity[0].participant.communicationUser.id, CALL_TARGET_ID); - assert.equal(data.recordingStateCallbackUri, CALL_CALLBACK_URL); - assert.equal(request.method, "POST"); - }); - - it("makes successful startRecording request", async function () { - const mockResponse: RestModel.RecordingStateResponse = { - recordingId: RECORDING_ID, - recordingState: RECORDING_STATE, - }; - - const mockHttpClient = generateHttpClient(200, mockResponse); - callRecording = createRecordingClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - const recOptions: StartRecordingOptions = { - recordingStateCallbackEndpointUrl: CALL_CALLBACK_URL, - callLocator: { id: CALL_SERVER_CALL_ID, kind: "serverCallLocator" }, - recordingChannel: "unmixed", - recordingFormat: "wav", - recordingContent: "audio", - }; - - await callRecording.start(recOptions); - const request = spy.getCall(0).args[0]; - const data = JSON.parse(request.body?.toString() || ""); - - assert.equal(data.callLocator.kind, "serverCallLocator"); - assert.equal(data.recordingStateCallbackUri, CALL_CALLBACK_URL); - assert.equal(request.method, "POST"); - }); - - it("makes successful getRecordingProperties request", async function () { - const mockResponse: RestModel.RecordingStateResponse = { - recordingId: RECORDING_ID, - recordingState: RECORDING_STATE, - }; - - const mockHttpClient = generateHttpClient(200, mockResponse); - callRecording = createRecordingClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - - await callRecording.getState(RECORDING_ID); - const request = spy.getCall(0).args[0]; - - assert.equal(request.method, "GET"); - }); - - it("Sends correct args to stop a recording", async () => { - const mockHttpClient = generateHttpClient(204); - callRecording = createRecordingClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - await callRecording.stop(RECORDING_ID); - const request = spy.getCall(0).args[0]; - assert.equal(request.method, "DELETE"); - }); - - it("Sends correct args to pause a recording", async () => { - const mockHttpClient = generateHttpClient(202); - callRecording = createRecordingClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - await callRecording.pause(RECORDING_ID); - const request = spy.getCall(0).args[0]; - assert.equal(request.method, "POST"); - }); - - it("Sends correct args to resume a recording", async () => { - const mockHttpClient = generateHttpClient(202); - callRecording = createRecordingClient(mockHttpClient); - const spy = sinon.spy(mockHttpClient, "sendRequest"); - await callRecording.resume(RECORDING_ID); - const request = spy.getCall(0).args[0]; - assert.equal(request.method, "POST"); - }); + let callRecording: CallRecording; + + afterEach(function () { + sinon.restore(); + }); + + it("can instantiate", async function () { + new CallRecording(baseUri, { key: generateToken() }); + }); + + it("makes successful startRecording request with channel affinity", async function () { + const mockResponse: RestModel.RecordingStateResponse = { + recordingId: RECORDING_ID, + recordingState: RECORDING_STATE, + }; + + const mockHttpClient = generateHttpClient(200, mockResponse); + callRecording = createRecordingClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const channelZeroParticipant: CommunicationIdentifier = { communicationUserId: CALL_TARGET_ID }; + const channelAffinity: ChannelAffinity = { + targetParticipant: channelZeroParticipant, + channel: 0, + }; + + const recOptions: StartRecordingOptions = { + recordingStateCallbackEndpointUrl: CALL_CALLBACK_URL, + callLocator: { id: CALL_SERVER_CALL_ID, kind: "serverCallLocator" }, + recordingChannel: "unmixed", + recordingFormat: "wav", + recordingContent: "audio", + channelAffinity: [channelAffinity], + }; + + await callRecording.start(recOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.callLocator.kind, "serverCallLocator"); + assert.equal(data.channelAffinity[0].channel, 0); + assert.equal(data.channelAffinity[0].participant.rawId, CALL_TARGET_ID); + assert.equal(data.channelAffinity[0].participant.kind, "communicationUser"); + assert.equal(data.channelAffinity[0].participant.communicationUser.id, CALL_TARGET_ID); + assert.equal(data.recordingStateCallbackUri, CALL_CALLBACK_URL); + assert.equal(request.method, "POST"); + }); + + it("makes successful startRecording request", async function () { + const mockResponse: RestModel.RecordingStateResponse = { + recordingId: RECORDING_ID, + recordingState: RECORDING_STATE, + }; + + const mockHttpClient = generateHttpClient(200, mockResponse); + callRecording = createRecordingClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + const recOptions: StartRecordingOptions = { + recordingStateCallbackEndpointUrl: CALL_CALLBACK_URL, + callLocator: { id: CALL_SERVER_CALL_ID, kind: "serverCallLocator" }, + recordingChannel: "unmixed", + recordingFormat: "wav", + recordingContent: "audio", + }; + + await callRecording.start(recOptions); + const request = spy.getCall(0).args[0]; + const data = JSON.parse(request.body?.toString() || ""); + + assert.equal(data.callLocator.kind, "serverCallLocator"); + assert.equal(data.recordingStateCallbackUri, CALL_CALLBACK_URL); + assert.equal(request.method, "POST"); + }); + + it("makes successful getRecordingProperties request", async function () { + const mockResponse: RestModel.RecordingStateResponse = { + recordingId: RECORDING_ID, + recordingState: RECORDING_STATE, + }; + + const mockHttpClient = generateHttpClient(200, mockResponse); + callRecording = createRecordingClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + + await callRecording.getState(RECORDING_ID); + const request = spy.getCall(0).args[0]; + + assert.equal(request.method, "GET"); + }); + + it("Sends correct args to stop a recording", async () => { + const mockHttpClient = generateHttpClient(204); + callRecording = createRecordingClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + await callRecording.stop(RECORDING_ID); + const request = spy.getCall(0).args[0]; + assert.equal(request.method, "DELETE"); + }); + + it("Sends correct args to pause a recording", async () => { + const mockHttpClient = generateHttpClient(202); + callRecording = createRecordingClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + await callRecording.pause(RECORDING_ID); + const request = spy.getCall(0).args[0]; + assert.equal(request.method, "POST"); + }); + + it("Sends correct args to resume a recording", async () => { + const mockHttpClient = generateHttpClient(202); + callRecording = createRecordingClient(mockHttpClient); + const spy = sinon.spy(mockHttpClient, "sendRequest"); + await callRecording.resume(RECORDING_ID); + const request = spy.getCall(0).args[0]; + assert.equal(request.method, "POST"); + }); }); describe("CallRecording Live Tests", function () { - let recorder: Recorder; - let callerCallAutomationClient: CallAutomationClient; - let receiverCallAutomationClient: CallAutomationClient; - let callConnection: CallConnection; - let testUser: CommunicationUserIdentifier; - let testUser2: CommunicationUserIdentifier; - let testName: string; - - beforeEach(async function (this: Context) { - recorder = await createRecorder(this.currentTest); - testUser = await createTestUser(recorder); - testUser2 = await createTestUser(recorder); - callerCallAutomationClient = createCallAutomationClient(recorder, testUser); - receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + let recorder: Recorder; + let callerCallAutomationClient: CallAutomationClient; + let receiverCallAutomationClient: CallAutomationClient; + let callConnection: CallConnection; + let testUser: CommunicationUserIdentifier; + let testUser2: CommunicationUserIdentifier; + let testName: string; + + beforeEach(async function (this: Context) { + recorder = await createRecorder(this.currentTest); + testUser = await createTestUser(recorder); + testUser2 = await createTestUser(recorder); + callerCallAutomationClient = createCallAutomationClient(recorder, testUser); + receiverCallAutomationClient = createCallAutomationClient(recorder, testUser2); + }); + + afterEach(async function (this: Context) { + persistEvents(testName); + serviceBusReceivers.forEach((receiver) => { + receiver.close(); }); - - afterEach(async function (this: Context) { - persistEvents(testName); - serviceBusReceivers.forEach((receiver) => { - receiver.close(); - }); - events.forEach((callConnectionEvents) => { - callConnectionEvents.clear(); - }); - events.clear(); - serviceBusReceivers.clear(); - incomingCallContexts.clear(); - await recorder.stop(); - if (callConnection) { - try { - await callConnection.hangUp(true); - } catch { - return; - } - } + events.forEach((callConnectionEvents) => { + callConnectionEvents.clear(); }); - - it("Creates a call, start recording, and hangs up", async function () { - testName = this.test?.fullTitle() - ? this.test?.fullTitle().replace(/ /g, "_") - : "create_call_start_recording_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: "recordingCreateCall" }; - - 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 answerCallOption: AnswerCallOptions = { operationContext: "recordingAnswer" }; - await receiverCallAutomationClient.answerCall( - incomingCallContext, - callBackUrl, - answerCallOption, - ); - } - const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); - - assert.isDefined(callConnectedEvent); - callConnection = result.callConnection; - - const playSource: FileSource[] = [ - { - url: fileSourceUrl, - kind: "fileSource", - }, - ]; - - // Call recording can fail when no audio is in call, we will play audio to avoid that. - const playToAllOptions: PlayOptions = { operationContext: "recordingPlay" }; - await callConnection.getCallMedia().playToAll(playSource, playToAllOptions); - - const recOptions: StartRecordingOptions = { - recordingStateCallbackEndpointUrl: callBackUrl, - callLocator: { - id: (await callConnection.getCallConnectionProperties()).serverCallId || "", - kind: "serverCallLocator", - }, - recordingChannel: "unmixed", - recordingFormat: "wav", - recordingContent: "audio", - }; - - const recordingStateResult = await callerCallAutomationClient - .getCallRecording() - .start(recOptions); - - // Delay for 6 seconds, this is to let the recording state change to active - await new Promise((resolve) => setTimeout(resolve, 6000)); - const recStatus = await callerCallAutomationClient - .getCallRecording() - .getState(recordingStateResult.recordingId); - assert.equal(recStatus.recordingState, "active"); - await callerCallAutomationClient.getCallRecording().stop(recordingStateResult.recordingId); - }).timeout(60000); + events.clear(); + serviceBusReceivers.clear(); + incomingCallContexts.clear(); + await recorder.stop(); + if (callConnection) { + try { + await callConnection.hangUp(true); + } catch { + return; + } + } + }); + + it("Creates a call, start recording, and hangs up", async function () { + testName = this.test?.fullTitle() + ? this.test?.fullTitle().replace(/ /g, "_") + : "create_call_start_recording_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: "recordingCreateCall" }; + + 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 answerCallOption: AnswerCallOptions = { operationContext: "recordingAnswer" }; + await receiverCallAutomationClient.answerCall( + incomingCallContext, + callBackUrl, + answerCallOption, + ); + } + const callConnectedEvent = await waitForEvent("CallConnected", callConnectionId, 8000); + + assert.isDefined(callConnectedEvent); + callConnection = result.callConnection; + + const playSource: FileSource[] = [ + { + url: fileSourceUrl, + kind: "fileSource", + }, + ]; + + // Call recording can fail when no audio is in call, we will play audio to avoid that. + const playToAllOptions: PlayOptions = { operationContext: "recordingPlay" }; + await callConnection.getCallMedia().playToAll(playSource, playToAllOptions); + + const recOptions: StartRecordingOptions = { + recordingStateCallbackEndpointUrl: callBackUrl, + callLocator: { + id: (await callConnection.getCallConnectionProperties()).serverCallId || "", + kind: "serverCallLocator", + }, + recordingChannel: "unmixed", + recordingFormat: "wav", + recordingContent: "audio", + }; + + const recordingStateResult = await callerCallAutomationClient + .getCallRecording() + .start(recOptions); + + // Delay for 6 seconds, this is to let the recording state change to active + await new Promise((resolve) => setTimeout(resolve, 6000)); + const recStatus = await callerCallAutomationClient + .getCallRecording() + .getState(recordingStateResult.recordingId); + assert.equal(recStatus.recordingState, "active"); + await callerCallAutomationClient.getCallRecording().stop(recordingStateResult.recordingId); + }).timeout(60000); }); diff --git a/sdk/communication/communication-call-automation/test/utils/mockClient.ts b/sdk/communication/communication-call-automation/test/utils/mockClient.ts index 3cd5ddda3365..d72023d8b50b 100644 --- a/sdk/communication/communication-call-automation/test/utils/mockClient.ts +++ b/sdk/communication/communication-call-automation/test/utils/mockClient.ts @@ -2,46 +2,46 @@ // Licensed under the MIT License. import { - HttpClient, - PipelineRequest, - PipelineResponse, - createHttpHeaders, + HttpClient, + PipelineRequest, + PipelineResponse, + createHttpHeaders, } from "@azure/core-rest-pipeline"; import { baseUri, CALL_CONNECTION_ID, generateToken } from "../utils/connectionUtils"; import { CallMedia } from "../../src/callMedia"; import { CallRecording } from "../../src/callRecording"; export const generateHttpClient = (status: number, parsedBody?: unknown): HttpClient => { - const mockHttpClient: HttpClient = { - async sendRequest(httpRequest: PipelineRequest): Promise { - return { - status: status, - headers: createHttpHeaders(), - request: httpRequest, - bodyAsText: JSON.stringify(parsedBody), - }; - }, - }; - return mockHttpClient; + const mockHttpClient: HttpClient = { + async sendRequest(httpRequest: PipelineRequest): Promise { + return { + status: status, + headers: createHttpHeaders(), + request: httpRequest, + bodyAsText: JSON.stringify(parsedBody), + }; + }, + }; + return mockHttpClient; }; export const createMediaClient = (mockHttpClient: HttpClient): CallMedia => { - return new CallMedia( - CALL_CONNECTION_ID, - baseUri, - { key: generateToken() }, - { - httpClient: mockHttpClient, - }, - ); + return new CallMedia( + CALL_CONNECTION_ID, + baseUri, + { key: generateToken() }, + { + httpClient: mockHttpClient, + }, + ); }; export const createRecordingClient = (mockHttpClient: HttpClient): CallRecording => { - return new CallRecording( - baseUri, - { key: generateToken() }, - { - httpClient: mockHttpClient, - }, - ); + return new CallRecording( + baseUri, + { key: generateToken() }, + { + httpClient: mockHttpClient, + }, + ); }; diff --git a/sdk/communication/communication-call-automation/test/utils/recordedClient.ts b/sdk/communication/communication-call-automation/test/utils/recordedClient.ts index 9a0041d1113f..5794da4b5cfa 100644 --- a/sdk/communication/communication-call-automation/test/utils/recordedClient.ts +++ b/sdk/communication/communication-call-automation/test/utils/recordedClient.ts @@ -5,342 +5,342 @@ import * as dotenv from "dotenv"; import { isNode } from "@azure/core-util"; import fs from "fs"; import { - Recorder, - RecorderStartOptions, - env, - assertEnvironmentVariable, - isRecordMode, - isPlaybackMode, + Recorder, + RecorderStartOptions, + env, + assertEnvironmentVariable, + isRecordMode, + isPlaybackMode, } from "@azure-tools/test-recorder"; import { Test } from "mocha"; import { generateToken } from "./connectionUtils"; import { - CommunicationIdentityClient, - CommunicationIdentityClientOptions, + CommunicationIdentityClient, + CommunicationIdentityClientOptions, } from "@azure/communication-identity"; import { - CommunicationUserIdentifier, - CommunicationIdentifier, - serializeCommunicationIdentifier, - isPhoneNumberIdentifier, - createIdentifierFromRawId, - CommunicationIdentifierKind, + CommunicationUserIdentifier, + CommunicationIdentifier, + serializeCommunicationIdentifier, + isPhoneNumberIdentifier, + createIdentifierFromRawId, + CommunicationIdentifierKind, } from "@azure/communication-common"; import { - CallAutomationClient, - CallAutomationClientOptions, - CallAutomationEvent, - parseCallAutomationEvent, + CallAutomationClient, + CallAutomationClientOptions, + CallAutomationEvent, + parseCallAutomationEvent, } from "../../src"; import { CommunicationIdentifierModel } from "../../src/generated/src"; import { assert } from "chai"; import { - createDefaultHttpClient, - createHttpHeaders, - createPipelineRequest, + createDefaultHttpClient, + createHttpHeaders, + createPipelineRequest, } from "@azure/core-rest-pipeline"; import { - ServiceBusClient, - ServiceBusReceiver, - ServiceBusReceivedMessage, - ProcessErrorArgs, + ServiceBusClient, + ServiceBusReceiver, + ServiceBusReceivedMessage, + ProcessErrorArgs, } from "@azure/service-bus"; import { PhoneNumbersClient, PhoneNumbersClientOptions } from "@azure/communication-phone-numbers"; if (isNode) { - dotenv.config(); + dotenv.config(); } const envSetupForPlayback: Record = { - COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING: "endpoint=https://endpoint/;accesskey=redacted", - DISPATCHER_ENDPOINT: "https://redacted.azurewebsites.net", - SERVICEBUS_STRING: - "Endpoint=sb://REDACTED.servicebus.windows.net/;SharedAccessKeyName=REDACTED;SharedAccessKey=REDACTED", - FILE_SOURCE_URL: "https://example.com/audio/test.wav", - TRANSPORT_URL: "https://REDACTED", - COGNITIVE_SERVICE_ENDPOINT: "https://REDACTED.cognitiveservices.azure.com/", + COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING: "endpoint=https://endpoint/;accesskey=redacted", + DISPATCHER_ENDPOINT: "https://redacted.azurewebsites.net", + SERVICEBUS_STRING: + "Endpoint=sb://REDACTED.servicebus.windows.net/;SharedAccessKeyName=REDACTED;SharedAccessKey=REDACTED", + FILE_SOURCE_URL: "https://example.com/audio/test.wav", + TRANSPORT_URL: "https://REDACTED", + COGNITIVE_SERVICE_ENDPOINT: "https://REDACTED.cognitiveservices.azure.com/", }; const fakeToken = generateToken(); const dispatcherEndpoint: string = !isPlaybackMode() - ? (env["DISPATCHER_ENDPOINT"] ?? envSetupForPlayback["DISPATCHER_ENDPOINT"]) - : envSetupForPlayback["DISPATCHER_ENDPOINT"]; + ? (env["DISPATCHER_ENDPOINT"] ?? envSetupForPlayback["DISPATCHER_ENDPOINT"]) + : envSetupForPlayback["DISPATCHER_ENDPOINT"]; const serviceBusConnectionString: string = !isPlaybackMode() - ? (env["SERVICEBUS_STRING"] ?? envSetupForPlayback["DISPATCHER_ENDPOINT"]) - : envSetupForPlayback["SERVICEBUS_STRING"]; + ? (env["SERVICEBUS_STRING"] ?? envSetupForPlayback["DISPATCHER_ENDPOINT"]) + : envSetupForPlayback["SERVICEBUS_STRING"]; export const fileSourceUrl: string = !isPlaybackMode() - ? (env["FILE_SOURCE_URL"] ?? envSetupForPlayback["DISPATCHER_ENDPOINT"]) - : envSetupForPlayback["FILE_SOURCE_URL"]; + ? (env["FILE_SOURCE_URL"] ?? envSetupForPlayback["DISPATCHER_ENDPOINT"]) + : envSetupForPlayback["FILE_SOURCE_URL"]; export const transportUrl: string = !isPlaybackMode() - ? (env["TRANSPORT_URL"] ?? envSetupForPlayback["TRANSPORT_URL"]) - : envSetupForPlayback["TRANSPORT_URL"]; + ? (env["TRANSPORT_URL"] ?? envSetupForPlayback["TRANSPORT_URL"]) + : envSetupForPlayback["TRANSPORT_URL"]; export const cognitiveServiceEndpoint: string = !isPlaybackMode() - ? (env["COGNITIVE_SERVICE_ENDPOINT"] ?? envSetupForPlayback["COGNITIVE_SERVICE_ENDPOINT"]) - : envSetupForPlayback["COGNITIVE_SERVICE_ENDPOINT"]; + ? (env["COGNITIVE_SERVICE_ENDPOINT"] ?? envSetupForPlayback["COGNITIVE_SERVICE_ENDPOINT"]) + : envSetupForPlayback["COGNITIVE_SERVICE_ENDPOINT"]; export const dispatcherCallback: string = dispatcherEndpoint + "/api/servicebuscallback/events"; export const serviceBusReceivers: Map = new Map< - string, - ServiceBusReceiver + string, + ServiceBusReceiver >(); export const incomingCallContexts: Map = new Map(); export const events: Map> = new Map< - string, - Map + string, + Map >(); export const eventsToPersist: string[] = []; const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); function removeAllNonChar(input: string): string { - const regex = new RegExp("[^a-zA-Z0-9_-]", "g"); - return input.replace(regex, ""); + const regex = new RegExp("[^a-zA-Z0-9_-]", "g"); + return input.replace(regex, ""); } function encodePhoneNumber(input: string): string { - // Enocding + to UTF-16 to match unique id with Service bus queue - return input.replace("+", "\\u002B"); + // Enocding + to UTF-16 to match unique id with Service bus queue + return input.replace("+", "\\u002B"); } export function parseIdsFromIdentifier(identifier: CommunicationIdentifier): string { - const communicationIdentifierModel: CommunicationIdentifierModel = - serializeCommunicationIdentifier(identifier); - assert.isDefined(communicationIdentifierModel?.rawId); - if (isPhoneNumberIdentifier(identifier)) { - return communicationIdentifierModel?.rawId - ? removeAllNonChar(encodePhoneNumber(communicationIdentifierModel.rawId)) - : ""; - } else { - return communicationIdentifierModel?.rawId - ? removeAllNonChar(communicationIdentifierModel.rawId) - : ""; - } + const communicationIdentifierModel: CommunicationIdentifierModel = + serializeCommunicationIdentifier(identifier); + assert.isDefined(communicationIdentifierModel?.rawId); + if (isPhoneNumberIdentifier(identifier)) { + return communicationIdentifierModel?.rawId + ? removeAllNonChar(encodePhoneNumber(communicationIdentifierModel.rawId)) + : ""; + } else { + return communicationIdentifierModel?.rawId + ? removeAllNonChar(communicationIdentifierModel.rawId) + : ""; + } } function createServiceBusClient(): ServiceBusClient { - return new ServiceBusClient(serviceBusConnectionString); + return new ServiceBusClient(serviceBusConnectionString); } export const recorderOptions: RecorderStartOptions = { - envSetupForPlayback, - sanitizerOptions: { - connectionStringSanitizers: [ - { - fakeConnString: envSetupForPlayback["COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"], - actualConnString: env["COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"] || undefined, - }, - ], - bodyKeySanitizers: [{ jsonPath: "$.accessToken.token", value: fakeToken }], - uriSanitizers: [{ regex: true, value: "https://endpoint", target: "https://([^/?]+)" }], - }, - removeCentralSanitizers: [ - "AZSDK3493", // .name in the body is not a secret and is listed below in the beforeEach section - "AZSDK3430", // .id in the body is not a secret and is listed below in the beforeEach section + envSetupForPlayback, + sanitizerOptions: { + connectionStringSanitizers: [ + { + fakeConnString: envSetupForPlayback["COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"], + actualConnString: env["COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"] || undefined, + }, ], + bodyKeySanitizers: [{ jsonPath: "$.accessToken.token", value: fakeToken }], + uriSanitizers: [{ regex: true, value: "https://endpoint", target: "https://([^/?]+)" }], + }, + removeCentralSanitizers: [ + "AZSDK3493", // .name in the body is not a secret and is listed below in the beforeEach section + "AZSDK3430", // .id in the body is not a secret and is listed below in the beforeEach section + ], }; export async function createRecorder(context: Test | undefined): Promise { - const recorder = new Recorder(context); - await recorder.start(recorderOptions); - await recorder.setMatcher("HeaderlessMatcher"); - return recorder; + const recorder = new Recorder(context); + await recorder.start(recorderOptions); + await recorder.setMatcher("HeaderlessMatcher"); + return recorder; } export async function createTestUser(recorder: Recorder): Promise { - const identityClient = new CommunicationIdentityClient( - assertEnvironmentVariable("COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"), - recorder.configureClientOptions({}) as CommunicationIdentityClientOptions, - ); - return identityClient.createUser(); + const identityClient = new CommunicationIdentityClient( + assertEnvironmentVariable("COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"), + recorder.configureClientOptions({}) as CommunicationIdentityClientOptions, + ); + return identityClient.createUser(); } export function createCallAutomationClient( - recorder: Recorder, - sourceIdentity: CommunicationUserIdentifier, + recorder: Recorder, + sourceIdentity: CommunicationUserIdentifier, ): CallAutomationClient { - const connectionString = assertEnvironmentVariable( - "COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING", - ); - const options: CallAutomationClientOptions = { - sourceIdentity: sourceIdentity, - }; - return new CallAutomationClient(connectionString, recorder.configureClientOptions(options)); + const connectionString = assertEnvironmentVariable( + "COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING", + ); + const options: CallAutomationClientOptions = { + sourceIdentity: sourceIdentity, + }; + return new CallAutomationClient(connectionString, recorder.configureClientOptions(options)); } async function eventBodyHandler(body: any): Promise { - if (body.incomingCallContext) { - const incomingCallContext: string = body.incomingCallContext; - const callerRawId: CommunicationIdentifierKind = createIdentifierFromRawId(body.from.rawId); - const calleeRawId: CommunicationIdentifierKind = createIdentifierFromRawId(body.to.rawId); - const key: string = parseIdsFromIdentifier(callerRawId) + parseIdsFromIdentifier(calleeRawId); - incomingCallContexts.set(key, incomingCallContext); - } else { - const event: CallAutomationEvent = await parseCallAutomationEvent(body); - if (event.callConnectionId) { - if (events.has(event.callConnectionId)) { - events.get(event.callConnectionId)?.set(event.kind, event); - } else { - const temp: Map = new Map(); - temp.set(event.kind, event); - events.set(event.callConnectionId, temp); - } - } + if (body.incomingCallContext) { + const incomingCallContext: string = body.incomingCallContext; + const callerRawId: CommunicationIdentifierKind = createIdentifierFromRawId(body.from.rawId); + const calleeRawId: CommunicationIdentifierKind = createIdentifierFromRawId(body.to.rawId); + const key: string = parseIdsFromIdentifier(callerRawId) + parseIdsFromIdentifier(calleeRawId); + incomingCallContexts.set(key, incomingCallContext); + } else { + const event: CallAutomationEvent = await parseCallAutomationEvent(body); + if (event.callConnectionId) { + if (events.has(event.callConnectionId)) { + events.get(event.callConnectionId)?.set(event.kind, event); + } else { + const temp: Map = new Map(); + temp.set(event.kind, event); + events.set(event.callConnectionId, temp); + } } + } } export async function serviceBusWithNewCall( - caller: CommunicationIdentifier, - receiver: CommunicationIdentifier, + caller: CommunicationIdentifier, + receiver: CommunicationIdentifier, ): Promise { - const callerId: string = parseIdsFromIdentifier(caller); - const receiverId: string = parseIdsFromIdentifier(receiver); - const uniqueId: string = callerId + receiverId; + const callerId: string = parseIdsFromIdentifier(caller); + const receiverId: string = parseIdsFromIdentifier(receiver); + const uniqueId: string = callerId + receiverId; - if (!isPlaybackMode()) { - // subscribe to event dispatcher - const dispatcherUrl: string = - dispatcherEndpoint + `/api/servicebuscallback/subscribe?q=${uniqueId}`; - try { - const client = createDefaultHttpClient(); - const request = createPipelineRequest({ - url: dispatcherUrl, - method: "POST", - body: JSON.stringify({}), - headers: createHttpHeaders({ - cookie: "", - }), - }); - request.allowInsecureConnection = true; - await client.sendRequest(request); - } catch (e) { - console.log("Error occurred at posting to dispatcher", e); - } + if (!isPlaybackMode()) { + // subscribe to event dispatcher + const dispatcherUrl: string = + dispatcherEndpoint + `/api/servicebuscallback/subscribe?q=${uniqueId}`; + try { + const client = createDefaultHttpClient(); + const request = createPipelineRequest({ + url: dispatcherUrl, + method: "POST", + body: JSON.stringify({}), + headers: createHttpHeaders({ + cookie: "", + }), + }); + request.allowInsecureConnection = true; + await client.sendRequest(request); + } catch (e) { + console.log("Error occurred at posting to dispatcher", e); + } - // create a service bus processor - const serviceBusClient = createServiceBusClient(); - const serviceBusReceiver: ServiceBusReceiver = serviceBusClient.createReceiver(uniqueId); + // create a service bus processor + const serviceBusClient = createServiceBusClient(); + const serviceBusReceiver: ServiceBusReceiver = serviceBusClient.createReceiver(uniqueId); - // function to handle messages - const messageHandler = async (messageReceived: ServiceBusReceivedMessage): Promise => { - if (isRecordMode()) { - const messageInString: string = JSON.stringify(messageReceived.body); - eventsToPersist.push(messageInString); - } - await eventBodyHandler(messageReceived.body); - }; + // function to handle messages + const messageHandler = async (messageReceived: ServiceBusReceivedMessage): Promise => { + if (isRecordMode()) { + const messageInString: string = JSON.stringify(messageReceived.body); + eventsToPersist.push(messageInString); + } + await eventBodyHandler(messageReceived.body); + }; - // function to handle any errors - const errorHandler = async (error: ProcessErrorArgs): Promise => { - console.log(error); - }; + // function to handle any errors + const errorHandler = async (error: ProcessErrorArgs): Promise => { + console.log(error); + }; - // subscribe and specify the message and error handlers - serviceBusReceiver.subscribe({ - processMessage: messageHandler, - processError: errorHandler, - }); + // subscribe and specify the message and error handlers + serviceBusReceiver.subscribe({ + processMessage: messageHandler, + processError: errorHandler, + }); - serviceBusReceivers.set(uniqueId, serviceBusReceiver); - } - return uniqueId; + serviceBusReceivers.set(uniqueId, serviceBusReceiver); + } + return uniqueId; } export async function waitForIncomingCallContext( - uniqueId: string, - timeOut: number, + uniqueId: string, + timeOut: number, ): Promise { - if (!isPlaybackMode()) { - let currentTime = new Date().getTime(); - const timeOutTime = currentTime + timeOut; - while (currentTime < timeOutTime) { - const incomingCallContext = incomingCallContexts.get(uniqueId); - if (incomingCallContext) { - return incomingCallContext; - } - await sleep(1000); - currentTime += 1000; - } + if (!isPlaybackMode()) { + let currentTime = new Date().getTime(); + const timeOutTime = currentTime + timeOut; + while (currentTime < timeOutTime) { + const incomingCallContext = incomingCallContexts.get(uniqueId); + if (incomingCallContext) { + return incomingCallContext; + } + await sleep(1000); + currentTime += 1000; } - return ""; + } + return ""; } export async function waitForEvent( - eventName: string, - callConnectionId: string, - timeOut: number, + eventName: string, + callConnectionId: string, + timeOut: number, ): Promise { - let currentTime = new Date().getTime(); - const timeOutTime = currentTime + timeOut; - while (currentTime < timeOutTime) { - const eventGroup = events.get(callConnectionId); - if (eventGroup && eventGroup.has(eventName)) { - return eventGroup.get(eventName); - } - await sleep(1000); - currentTime += 1000; + let currentTime = new Date().getTime(); + const timeOutTime = currentTime + timeOut; + while (currentTime < timeOutTime) { + const eventGroup = events.get(callConnectionId); + if (eventGroup && eventGroup.has(eventName)) { + return eventGroup.get(eventName); } - return undefined; + await sleep(1000); + currentTime += 1000; + } + return undefined; } export function persistEvents(testName: string): void { - if (isRecordMode()) { - // sanitize the events values accordingly - const sanatizedEvents: any[] = []; - for (const event of eventsToPersist) { - const jsonData = JSON.parse(event); - sanitizeObject(jsonData, ["rawId", "id", "incomingCallContext", "value", "serverCallId"]); - sanatizedEvents.push(jsonData); - } + if (isRecordMode()) { + // sanitize the events values accordingly + const sanatizedEvents: any[] = []; + for (const event of eventsToPersist) { + const jsonData = JSON.parse(event); + sanitizeObject(jsonData, ["rawId", "id", "incomingCallContext", "value", "serverCallId"]); + sanatizedEvents.push(jsonData); + } - const jsonArrayString = JSON.stringify(sanatizedEvents, null, 2); - fs.writeFile(`recordings\\${testName}.json`, jsonArrayString, (err) => { - if (err) throw err; - }); - // Clear the array for next test to use - while (eventsToPersist.length > 0) { - eventsToPersist.pop(); - } + const jsonArrayString = JSON.stringify(sanatizedEvents, null, 2); + fs.writeFile(`recordings\\${testName}.json`, jsonArrayString, (err) => { + if (err) throw err; + }); + // Clear the array for next test to use + while (eventsToPersist.length > 0) { + eventsToPersist.pop(); } + } } export async function loadPersistedEvents(testName: string): Promise { - if (isPlaybackMode()) { - let data: string = ""; - // Different OS has differnt file system path format. - try { - data = fs.readFileSync(`recordings\\${testName}.json`, "utf-8"); - } catch (e) { - console.log("original path doesn't work"); - data = fs.readFileSync(`recordings/${testName}.json`, "utf-8"); - } - const loadedEvents = JSON.parse(data); - - loadedEvents.forEach(async (oneEvent: any) => { - await eventBodyHandler(oneEvent); - }); + if (isPlaybackMode()) { + let data: string = ""; + // Different OS has differnt file system path format. + try { + data = fs.readFileSync(`recordings\\${testName}.json`, "utf-8"); + } catch (e) { + console.log("original path doesn't work"); + data = fs.readFileSync(`recordings/${testName}.json`, "utf-8"); } + const loadedEvents = JSON.parse(data); + + loadedEvents.forEach(async (oneEvent: any) => { + await eventBodyHandler(oneEvent); + }); + } } export async function getPhoneNumbers(recorder: Recorder): Promise { - const phoneNumbersClient = new PhoneNumbersClient( - assertEnvironmentVariable("COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"), - recorder.configureClientOptions({}) as PhoneNumbersClientOptions, - ); - const purchasedPhoneNumbers = phoneNumbersClient.listPurchasedPhoneNumbers(); - const phoneNumbers: string[] = []; - for await (const purchasedNumber of purchasedPhoneNumbers) { - phoneNumbers.push(purchasedNumber.phoneNumber); - } - return phoneNumbers; + const phoneNumbersClient = new PhoneNumbersClient( + assertEnvironmentVariable("COMMUNICATION_LIVETEST_STATIC_CONNECTION_STRING"), + recorder.configureClientOptions({}) as PhoneNumbersClientOptions, + ); + const purchasedPhoneNumbers = phoneNumbersClient.listPurchasedPhoneNumbers(); + const phoneNumbers: string[] = []; + for await (const purchasedNumber of purchasedPhoneNumbers) { + phoneNumbers.push(purchasedNumber.phoneNumber); + } + return phoneNumbers; } function sanitizeObject(obj: any, keysToSanitize: string[]) { - for (const key in obj) { - if (typeof obj[key] === "object") { - sanitizeObject(obj[key], keysToSanitize); - } else { - // Replace keys in the keysToSanitize array with 'sanitized' - if (keysToSanitize.includes(key)) { - obj[key] = "sanitized"; - } - } + for (const key in obj) { + if (typeof obj[key] === "object") { + sanitizeObject(obj[key], keysToSanitize); + } else { + // Replace keys in the keysToSanitize array with 'sanitized' + if (keysToSanitize.includes(key)) { + obj[key] = "sanitized"; + } } + } }