diff --git a/sdk/communication/communication-callingserver/review/communication-callingserver.api.md b/sdk/communication/communication-callingserver/review/communication-callingserver.api.md index 208a0b797fc7..227affc919b5 100644 --- a/sdk/communication/communication-callingserver/review/communication-callingserver.api.md +++ b/sdk/communication/communication-callingserver/review/communication-callingserver.api.md @@ -9,19 +9,34 @@ import * as coreHttp from '@azure/core-http'; import { OperationOptions } from '@azure/core-http'; import { PhoneNumberIdentifier } from '@azure/communication-common'; import { PipelineOptions } from '@azure/core-http'; +import { RestResponse } from '@azure/core-http'; import { TokenCredential } from '@azure/core-auth'; // @public export type AddParticipantOptions = OperationOptions; +// @public +export interface AddParticipantResult { + participantId?: string; +} + +// @public (undocumented) +export interface AddParticipantResultEvent { + operationContext?: string; + // Warning: (ae-forgotten-export) The symbol "ResultInfo" needs to be exported by the entry point index.d.ts + resultInfo?: ResultInfo; + // Warning: (ae-forgotten-export) The symbol "OperationStatus" needs to be exported by the entry point index.d.ts + status: OperationStatus; +} + // @public export class CallConnection { // Warning: (ae-forgotten-export) The symbol "CallConnections" needs to be exported by the entry point index.d.ts constructor(callConnectionId: string, callConnectionRestClient: CallConnections); - // Warning: (ae-forgotten-export) The symbol "AddParticipantResult" needs to be exported by the entry point index.d.ts - addParticipant(participant: CommunicationIdentifier, alternateCallerId?: string, operationContext?: string, options?: AddParticipantOptions): Promise; - cancelAllMediaOperations(operationContext?: string, options?: CancelAllMediaOperationsOptions): Promise; + addParticipant(participant: CommunicationIdentifier, alternateCallerId?: string, operationContext?: string, options?: AddParticipantOptions): Promise; + cancelAllMediaOperations(operationContext?: string, options?: CancelAllMediaOperationsOptions): Promise; cancelParticipantMediaOperation(participant: CommunicationIdentifier, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; + getCallConnectionId(): string; hangUp(options?: HangUpOptions): Promise; playAudio(audioFileUri: string, options: PlayAudioOptions): Promise; playAudioToParticipant(participant: CommunicationIdentifier, audioFileUri: string, options: PlayAudioOptions): Promise; @@ -29,20 +44,62 @@ export class CallConnection { transferCall(targetParticipant: CommunicationIdentifier, userToUserInformation: string, options?: TransferCallOptions): Promise; } +// @public +export type CallConnectionsAddParticipantResponse = AddParticipantResult & { + _response: coreHttp.HttpResponse & { + bodyAsText: string; + parsedBody: AddParticipantResult; + }; +}; + +// Warning: (ae-forgotten-export) The symbol "CancelAllMediaOperationsResult" needs to be exported by the entry point index.d.ts +// +// @public +export type CallConnectionsCancelAllMediaOperationsResponse = CancelAllMediaOperationsResult & { + _response: coreHttp.HttpResponse & { + bodyAsText: string; + parsedBody: CancelAllMediaOperationsResult; + }; +}; + +// @public +export type CallConnectionsPlayAudioResponse = PlayAudioResult & { + _response: coreHttp.HttpResponse & { + bodyAsText: string; + parsedBody: PlayAudioResult; + }; +}; + +// @public +export interface CallConnectionStateChangedEvent { + callConnectionId?: string; + // Warning: (ae-forgotten-export) The symbol "CallConnectionState" needs to be exported by the entry point index.d.ts + callConnectionState: CallConnectionState; + serverCallId?: string; +} + // @public export class CallingServerClient { constructor(connectionString: string, options?: CallingServerClientOptions); constructor(endpoint: string, credential: TokenCredential, options?: CallingServerClientOptions); - addParticipant(callLocator: CallLocator, participant: CommunicationIdentifier, callbackUri: string, alternateCallerId?: string, operationContext?: string, options?: AddParticipantOptions): Promise; + // Warning: (ae-forgotten-export) The symbol "ServerCallsAddParticipantResponse" needs to be exported by the entry point index.d.ts + addParticipant(callLocator: CallLocator, participant: CommunicationIdentifier, callbackUri: string, alternateCallerId?: string, operationContext?: string, options?: AddParticipantOptions): Promise; cancelMediaOperation(callLocator: CallLocator, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; cancelParticipantMediaOperation(callLocator: CallLocator, participant: CommunicationIdentifier, mediaOperationId: string, options?: CancelMediaOperationOptions): Promise; createCallConnection(source: CommunicationIdentifier, targets: CommunicationIdentifier[], options: CreateCallOptions): Promise; getCallConnection(callConnectionId: string): CallConnection; + // Warning: (ae-forgotten-export) The symbol "CallRecordingProperties" needs to be exported by the entry point index.d.ts + getRecordingProperties(recordingId: string, options?: GetRecordingPropertiesOptions): Promise; joinCall(callLocator: CallLocator, source: CommunicationIdentifier, options: JoinCallOptions): Promise; + pauseRecording(recordingId: string, options?: PauseRecordingOptions): Promise; playAudio(callLocator: CallLocator, audioFileUri: string, options: PlayAudioOptions): Promise; playAudioToParticipant(callLocator: CallLocator, participant: CommunicationIdentifier, audioFileUri: string, options: PlayAudioOptions): Promise; removeParticipant(callLocator: CallLocator, participant: CommunicationIdentifier, options?: RemoveParticipantOptions): Promise; - } + resumeRecording(recordingId: string, options?: ResumeRecordingOptions): Promise; + // Warning: (ae-forgotten-export) The symbol "StartCallRecordingResult" needs to be exported by the entry point index.d.ts + startRecording(callLocator: CallLocator, recordingStateCallbackUri: string, options?: StartRecordingOptions): Promise; + stopRecording(recordingId: string, options?: StopRecordingOptions): Promise; +} // @public export interface CallingServerClientOptions extends PipelineOptions { @@ -60,6 +117,20 @@ export type CancelAllMediaOperationsOptions = OperationOptions; // @public export type CancelMediaOperationOptions = OperationOptions; +// @public +export interface CommunicationIdentifierModel { + communicationUser?: CommunicationUserIdentifierModel; + // Warning: (ae-forgotten-export) The symbol "MicrosoftTeamsUserIdentifierModel" needs to be exported by the entry point index.d.ts + microsoftTeamsUser?: MicrosoftTeamsUserIdentifierModel; + phoneNumber?: PhoneNumberIdentifierModel; + rawId?: string; +} + +// @public +export interface CommunicationUserIdentifierModel { + id: string; +} + // @public export interface CreateCallOptions extends OperationOptions { alternateCallerId?: PhoneNumberIdentifier; @@ -72,14 +143,17 @@ export interface CreateCallOptions extends OperationOptions { // @public export const enum EventSubscriptionType { // (undocumented) - DtmfReceived = "dtmfReceived", + ParticipantsUpdated = "participantsUpdated", // (undocumented) - ParticipantsUpdated = "participantsUpdated" + ToneReceived = "toneReceived" } // @public export const getLocatorKind: (locator: CallLocator) => CallLocatorKind; +// @public +export type GetRecordingPropertiesOptions = OperationOptions; + // @public export interface GroupCallLocator { groupCallId: string; @@ -107,6 +181,90 @@ export interface JoinCallOptions extends OperationOptions { subject?: string; } +// @public +export const enum KnownCallConnectionState { + // (undocumented) + Connected = "connected", + // (undocumented) + Connecting = "connecting", + // (undocumented) + Disconnected = "disconnected", + // (undocumented) + Disconnecting = "disconnecting", + // (undocumented) + TransferAccepted = "transferAccepted", + // (undocumented) + Transferring = "transferring" +} + +// @public (undocumented) +export class KnownCallingServerEventType { + // (undocumented) + static ADD_PARTICIPANT_RESULT_EVENT: string | null; + // (undocumented) + static CALL_CONNECTION_STATE_CHANGED_EVENT: string | null; + // (undocumented) + static CALL_RECORDING_STATE_CHANGED_EVENT: string | null; + // (undocumented) + static fromString(value: string): string | null; + // (undocumented) + static PARTICIPANTS_UPDATED_EVENT: string | null; + // (undocumented) + static PLAY_AUDIO_RESULT_EVENT: string | null; + // (undocumented) + static TONE_RECEIVED_EVENT: string | null; +} + +// @public +export const enum KnownOperationStatus { + // (undocumented) + Completed = "completed", + // (undocumented) + Failed = "failed", + // (undocumented) + NotStarted = "notStarted", + // (undocumented) + Running = "running" +} + +// @public +export const enum KnownToneValue { + // (undocumented) + A = "a", + // (undocumented) + B = "b", + // (undocumented) + C = "c", + // (undocumented) + D = "d", + // (undocumented) + Flash = "flash", + // (undocumented) + Pound = "pound", + // (undocumented) + Star = "star", + // (undocumented) + Tone0 = "tone0", + // (undocumented) + Tone1 = "tone1", + // (undocumented) + Tone2 = "tone2", + // (undocumented) + Tone3 = "tone3", + // (undocumented) + Tone4 = "tone4", + // (undocumented) + Tone5 = "tone5", + // (undocumented) + Tone6 = "tone6", + // (undocumented) + Tone7 = "tone7", + // (undocumented) + Tone8 = "tone8", + // (undocumented) + Tone9 = "tone9" +} + // @public export const enum MediaType { // (undocumented) @@ -115,6 +273,14 @@ export const enum MediaType { Video = "video" } +// @public +export type PauseRecordingOptions = OperationOptions; + +// @public +export interface PhoneNumberIdentifierModel { + value: string; +} + // @public (undocumented) export interface PlayAudioOptions extends OperationOptions { audioFileId: string; @@ -127,15 +293,23 @@ export interface PlayAudioOptions extends OperationOptions { export interface PlayAudioResult { operationContext?: string; operationId?: string; - // Warning: (ae-forgotten-export) The symbol "ResultInfo" needs to be exported by the entry point index.d.ts resultInfo?: ResultInfo; - // Warning: (ae-forgotten-export) The symbol "OperationStatus" needs to be exported by the entry point index.d.ts + status: OperationStatus; +} + +// @public +export interface PlayAudioResultEvent { + operationContext?: string; + resultInfo?: ResultInfo; status: OperationStatus; } // @public export type RemoveParticipantOptions = OperationOptions; +// @public +export type ResumeRecordingOptions = OperationOptions; + // @public export interface ServerCallLocator { serverCallId: string; @@ -146,6 +320,25 @@ export interface ServerCallLocatorKind extends ServerCallLocator { kind: "serverCall"; } +// @public +export type StartRecordingOptions = OperationOptions; + +// @public +export type StopRecordingOptions = OperationOptions; + +// @public +export interface ToneInfo { + sequenceId: number; + // Warning: (ae-forgotten-export) The symbol "ToneValue" needs to be exported by the entry point index.d.ts + tone: ToneValue; +} + +// @public +export interface ToneReceivedEvent { + callConnectionId?: string; + toneInfo: ToneInfo; +} + // @public export type TransferCallOptions = OperationOptions; diff --git a/sdk/communication/communication-callingserver/src/callConnection.ts b/sdk/communication/communication-callingserver/src/callConnection.ts index 0a1223a369a4..8e90a0f6bc9b 100644 --- a/sdk/communication/communication-callingserver/src/callConnection.ts +++ b/sdk/communication/communication-callingserver/src/callConnection.ts @@ -8,11 +8,12 @@ import { PlayAudioRequest, PlayAudioResult, AddParticipantRequest, - AddParticipantResult, + CallConnectionsAddParticipantResponse, RemoveParticipantRequest, PlayAudioToParticipantRequest, CancelParticipantMediaOperationRequest, - TransferCallRequest + TransferCallRequest, + CallConnectionsCancelAllMediaOperationsResponse } from "./generated/src/models"; import { HangUpOptions, @@ -22,8 +23,8 @@ import { RemoveParticipantOptions, CancelMediaOperationOptions, TransferCallOptions - } from "./models"; - import { +} from "./models"; +import { CommunicationIdentifier, serializeCommunicationIdentifier } from "@azure/communication-common"; @@ -32,6 +33,7 @@ import { createSpan } from "./tracing"; import { operationOptionsToRequestOptionsBase } from "@azure/core-http"; import { SpanStatusCode } from "@azure/core-tracing"; import { extractOperationOptions } from "./extractOperationOptions"; +import { CallingServerUtils } from "./utils/utils" /** * The client to do call connection operations @@ -45,6 +47,13 @@ export class CallConnection { this.callConnectionRestClient = callConnectionRestClient; } + /** + * Returns the call connection id. + */ + public getCallConnectionId(): string { + return this.callConnectionId; + } + /** * Disconnect the current caller in a group-call or end a p2p-call. * @@ -77,10 +86,11 @@ export class CallConnection { * @param operationContext - The operation context. * @param options - Additional request options contains hangUp api options. */ + public async cancelAllMediaOperations( operationContext?: string, options: CancelAllMediaOperationsOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("CallConnectionRestClient-cancelAllMediaOperations", options); const request: CancelAllMediaOperationsRequest = { @@ -88,11 +98,12 @@ export class CallConnection { }; try { - await this.callConnectionRestClient.cancelAllMediaOperations( + const result = await this.callConnectionRestClient.cancelAllMediaOperations( this.callConnectionId, request, operationOptionsToRequestOptionsBase(updatedOptions) ); + return result; } catch (e) { span.setStatus({ code: SpanStatusCode.ERROR, @@ -110,10 +121,10 @@ export class CallConnection { * @param audioFileUri - The id for the media in the AudioFileUri, using which we cache the media resource. * @param options - Additional request options contains playAudio api options. */ - public async playAudio( + public async playAudio( audioFileUri: string, options: PlayAudioOptions - ): Promise { + ): Promise { const { operationOptions, restOptions } = extractOperationOptions(options); const { span, updatedOptions } = createSpan("CallConnectionRestClient-playAudio", operationOptions); @@ -124,7 +135,15 @@ export class CallConnection { audioFileId: restOptions.audioFileId, callbackUri: restOptions.callbackUri }; - + if (!CallingServerUtils.isValidUrl(audioFileUri)) { + throw new Error('audioFileUri is invalid.') + } + if (!(typeof options.audioFileId !== 'undefined' && options.audioFileId && options.audioFileId.trim())) { + throw new Error('audioFileId is invalid.') + } + if (!CallingServerUtils.isValidUrl(String(options.callbackUri))) { + throw new Error('callbackUri is invalid.') + } try { const response = await this.callConnectionRestClient.playAudio( this.callConnectionId, @@ -151,12 +170,12 @@ export class CallConnection { * @param operationContext - The operation context. * @param options - Additional request options contains addParticipant api options. */ - public async addParticipant( + public async addParticipant( participant: CommunicationIdentifier, alternateCallerId?: string, operationContext?: string, options: AddParticipantOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("CallConnectionRestClient-playAudio", options); var alternate_caller_id = typeof alternateCallerId === "undefined" ? alternateCallerId : serializeCommunicationIdentifier({ phoneNumber: alternateCallerId }).phoneNumber; @@ -190,10 +209,10 @@ export class CallConnection { * @param participant - The identifier of the participant. * @param options - Additional request options contains removeParticipant api options. */ - public async removeParticipant( + public async removeParticipant( participant: CommunicationIdentifier, options: RemoveParticipantOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("CallConnectionRestClient-removeParticipant", options); const request: RemoveParticipantRequest = { @@ -228,7 +247,7 @@ export class CallConnection { participant: CommunicationIdentifier, audioFileUri: string, options: PlayAudioOptions - ): Promise { + ): Promise { const { operationOptions, restOptions } = extractOperationOptions(options); const { span, updatedOptions } = createSpan("CallConnectionRestClient-playAudio", operationOptions); @@ -266,11 +285,11 @@ export class CallConnection { * @param mediaOperationId - The operationId of the media operation to cancel. * @param options - Additional request options contains cancelMediaOperation api options. */ - public async cancelParticipantMediaOperation( + public async cancelParticipantMediaOperation( participant: CommunicationIdentifier, mediaOperationId: string, options: CancelMediaOperationOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("CallConnectionRestClient-cancelParticipantMediaOperation", options); const request: CancelParticipantMediaOperationRequest = { @@ -306,7 +325,7 @@ export class CallConnection { targetParticipant: CommunicationIdentifier, userToUserInformation: string, options: TransferCallOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("CallConnectionRestClient-transferCall", options); const request: TransferCallRequest = { diff --git a/sdk/communication/communication-callingserver/src/callingServerClient.ts b/sdk/communication/communication-callingserver/src/callingServerClient.ts index 701468796030..2ab9aff7bc76 100644 --- a/sdk/communication/communication-callingserver/src/callingServerClient.ts +++ b/sdk/communication/communication-callingserver/src/callingServerClient.ts @@ -10,7 +10,12 @@ import { PlayAudioOptions, AddParticipantOptions, RemoveParticipantOptions, - CancelMediaOperationOptions + CancelMediaOperationOptions, + StartRecordingOptions, + PauseRecordingOptions, + ResumeRecordingOptions, + StopRecordingOptions, + GetRecordingPropertiesOptions } from "./models"; import { CallConnections, ServerCalls } from "./generated/src/operations"; import { @@ -19,11 +24,15 @@ import { PlayAudioWithCallLocatorRequest, PlayAudioResult, PlayAudioToParticipantWithCallLocatorRequest, - AddParticipantResult, + ServerCallsAddParticipantResponse, AddParticipantWithCallLocatorRequest, RemoveParticipantWithCallLocatorRequest, CancelMediaOperationWithCallLocatorRequest, - CancelParticipantMediaOperationWithCallLocatorRequest + CancelParticipantMediaOperationWithCallLocatorRequest, + StartCallRecordingRequest, + StartCallRecordingResult, + StartCallRecordingWithCallLocatorRequest, + CallRecordingProperties } from "./generated/src/models"; import { TokenCredential } from "@azure/core-auth"; @@ -38,7 +47,8 @@ import { PipelineOptions, InternalPipelineOptions, createPipelineFromOptions, - operationOptionsToRequestOptionsBase + operationOptionsToRequestOptionsBase, + RestResponse } from "@azure/core-http"; import { SpanStatusCode } from "@azure/core-tracing"; import { CallingServerApiClient } from "./generated/src/callingServerApiClient"; @@ -46,11 +56,12 @@ import { SDK_VERSION } from "./constants"; import { createSpan } from "./tracing"; import { logger } from "./logger"; import { extractOperationOptions } from "./extractOperationOptions"; +import { CallingServerUtils } from './utils/utils' /** * Client options used to configure CallingServer Client API requests. */ -export interface CallingServerClientOptions extends PipelineOptions {} +export interface CallingServerClientOptions extends PipelineOptions { } /** * Checks whether the type of a value is CallingServerClientOptions or not. @@ -88,7 +99,7 @@ export class CallingServerClient { connectionStringOrUrl: string, credentialOrOptions?: TokenCredential | CallingServerClientOptions, maybeOptions: CallingServerClientOptions = {} - ) { + ) { const { url, credential } = parseClientArguments(connectionStringOrUrl, credentialOrOptions); const options = isCallingServerClientOptions(credentialOrOptions) ? credentialOrOptions : maybeOptions; const libInfo = `azsdk-js-communication-callingserver/${SDK_VERSION}`; @@ -137,7 +148,7 @@ export class CallingServerClient { source: CommunicationIdentifier, targets: CommunicationIdentifier[], options: CreateCallOptions - ): Promise { + ): Promise { const { operationOptions, restOptions } = extractOperationOptions(options); const { span, updatedOptions } = createSpan( "CallConnectionRestClient-CreateCallConnection", @@ -151,7 +162,7 @@ export class CallingServerClient { requestedMediaTypes: restOptions.requestedMediaTypes, requestedCallEvents: restOptions.requestedCallEvents, alternateCallerId: - restOptions.alternateCallerId == null + restOptions.alternateCallerId == null ? undefined : { value: restOptions.alternateCallerId.phoneNumber }, subject: restOptions.subject @@ -178,13 +189,13 @@ export class CallingServerClient { } } - /** - * Join the call using callLocator. - * - * @param callLocator - The callLocator contains call id. - * @param source - The source identity. - * @param options - Additional request options contains joinCall api options. - */ + /** + * Join the call using callLocator. + * + * @param callLocator - The callLocator contains call id. + * @param source - The source identity. + * @param options - Additional request options contains joinCall api options. + */ public async joinCall( callLocator: CallLocator, source: CommunicationIdentifier, @@ -235,10 +246,21 @@ export class CallingServerClient { callLocator: CallLocator, audioFileUri: string, options: PlayAudioOptions - ): Promise { + ): Promise { const { operationOptions, restOptions } = extractOperationOptions(options); const { span, updatedOptions } = createSpan("ServerCallRestClient-playAudio", operationOptions); - + if (!CallingServerUtils.isValidUrl(audioFileUri)) { + throw new Error('audioFileUri is invalid.') + } + if (typeof options.audioFileId === 'undefined' || !options.audioFileId || !options.audioFileId.trim()) { + throw new Error('audioFileId is invalid.') + } + if (!CallingServerUtils.isValidUrl(String(options.callbackUri))) { + throw new Error('callbackUri is invalid.') + } + if (typeof options.operationContext === 'undefined' || !options.operationContext || !options.operationContext.trim()) { + throw new Error('operationContext can not be null.') + } const request: PlayAudioWithCallLocatorRequest = { callLocator: callLocator, playAudioRequest: { @@ -280,7 +302,7 @@ export class CallingServerClient { participant: CommunicationIdentifier, audioFileUri: string, options: PlayAudioOptions - ): Promise { + ): Promise { const { operationOptions, restOptions } = extractOperationOptions(options); const { span, updatedOptions } = createSpan("ServerCallRestClient-playAudio", operationOptions); @@ -330,7 +352,7 @@ export class CallingServerClient { alternateCallerId?: string, operationContext?: string, options: AddParticipantOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-playAudio", options); var alternate_caller_id = typeof alternateCallerId === "undefined" ? alternateCallerId : serializeCommunicationIdentifier({ phoneNumber: alternateCallerId }).phoneNumber; @@ -368,11 +390,11 @@ export class CallingServerClient { * @param participant - The identifier of the participant. * @param options - Additional request options contains removeParticipant api options. */ - public async removeParticipant( + public async removeParticipant( callLocator: CallLocator, participant: CommunicationIdentifier, options: RemoveParticipantOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-removeParticipant", options); const request: RemoveParticipantWithCallLocatorRequest = { @@ -405,11 +427,11 @@ export class CallingServerClient { * @param mediaOperationId - The operationId of the media operation to cancel. * @param options - Additional request options contains cancelMediaOperation api options. */ - public async cancelMediaOperation( + public async cancelMediaOperation( callLocator: CallLocator, mediaOperationId: string, options: CancelMediaOperationOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-cancelMediaOperation", options); const request: CancelMediaOperationWithCallLocatorRequest = { @@ -443,12 +465,12 @@ export class CallingServerClient { * @param mediaOperationId - The operationId of the media operation to cancel. * @param options - Additional request options contains cancelMediaOperation api options. */ - public async cancelParticipantMediaOperation( + public async cancelParticipantMediaOperation( callLocator: CallLocator, participant: CommunicationIdentifier, mediaOperationId: string, options: CancelMediaOperationOptions = {} - ): Promise { + ): Promise { const { span, updatedOptions } = createSpan("ServerCallRestClient-cancelParticipantMediaOperation", options); const request: CancelParticipantMediaOperationWithCallLocatorRequest = { @@ -474,4 +496,183 @@ export class CallingServerClient { span.end(); } } + + /** + * Start recording operation using call locator. + * + * @param callLocator - The callLocator contains server call id. + * @param recordingStateCallbackUri - The call back uri for recording state. + * @param options - Additional request options contains StartRecording api options. + */ + public async startRecording( + callLocator: CallLocator, + recordingStateCallbackUri: string, + options: StartRecordingOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("ServerCallRestClient-StartRecording", options); + + if (typeof callLocator === 'undefined' || !callLocator) { + throw new Error('callLocator is invalid.'); + } + if (typeof recordingStateCallbackUri === 'undefined' || + !recordingStateCallbackUri || + !CallingServerUtils.isValidUrl(recordingStateCallbackUri)) { + throw new Error('recordingStateCallbackUri is invalid.'); + } + + var startCallRecordingRequest: StartCallRecordingRequest = { recordingStateCallbackUri: recordingStateCallbackUri }; + + var startCallRecordingWithCallLocatorRequest: StartCallRecordingWithCallLocatorRequest = { + callLocator: callLocator, + 'startCallRecordingRequest': startCallRecordingRequest + }; + + try { + const result = await this.serverCallRestClient.startRecording( + startCallRecordingWithCallLocatorRequest, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + + /** + * Pause recording operation using recording id. + * + * @param recordingId - The recording id of the ongoing recording. + * @param options - Additional request options contains PauseRecording api options. + */ + public async pauseRecording( + recordingId: string, + options: PauseRecordingOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("ServerCallRestClient-PauseRecording", options); + + if (typeof recordingId === 'undefined' || !recordingId || !recordingId.trim()) { + throw new Error('recordingId is invalid.') + } + + try { + const result = await this.serverCallRestClient.pauseRecording( + recordingId, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + + /** + * Resume recording operation using recording id. + * + * @param recordingId - The recording id of the ongoing recording. + * @param options - Additional request options contains ResumeRecording api options. + */ + public async resumeRecording( + recordingId: string, + options: ResumeRecordingOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("ServerCallRestClient-ResumeRecording", options); + + if (typeof recordingId === 'undefined' || !recordingId || !recordingId.trim()) { + throw new Error('recordingId is invalid.') + } + + try { + const result = await this.serverCallRestClient.resumeRecording( + recordingId, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + + /** + * Stop recording operation using recording id. + * + * @param recordingId - The recording id of the ongoing recording. + * @param options - Additional request options contains StopRecording api options. + */ + public async stopRecording( + recordingId: string, + options: StopRecordingOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("ServerCallRestClient-StopRecording", options); + + if (typeof recordingId === 'undefined' || !recordingId || !recordingId.trim()) { + throw new Error('recordingId is invalid.') + } + + try { + const result = await this.serverCallRestClient.stopRecording( + recordingId, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + + /** + * Get recording properties using recording id. + * + * @param recordingId - The recording id of the ongoing recording. + * @param options - Additional request options contains GetRecordingState api options. + */ + public async getRecordingProperties( + recordingId: string, + options: GetRecordingPropertiesOptions = {} + ): Promise { + const { span, updatedOptions } = createSpan("ServerCallRestClient-Recording", options); + + if (typeof recordingId === 'undefined' || !recordingId || !recordingId.trim()) { + throw new Error('recordingId is invalid.') + } + + try { + const result = await this.serverCallRestClient.getRecordingProperties( + recordingId, + operationOptionsToRequestOptionsBase(updatedOptions) + ); + return result; + } catch (e) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } } diff --git a/sdk/communication/communication-callingserver/src/models.ts b/sdk/communication/communication-callingserver/src/models.ts index 326d51ca1a6d..6a9068261036 100644 --- a/sdk/communication/communication-callingserver/src/models.ts +++ b/sdk/communication/communication-callingserver/src/models.ts @@ -3,7 +3,24 @@ import { OperationOptions } from "@azure/core-http"; import { PhoneNumberIdentifier } from "@azure/communication-common"; -export { PlayAudioResult } from "./generated/src/models"; +export { + PlayAudioResult, + PlayAudioResultEvent, + AddParticipantResult, + ToneReceivedEvent, + AddParticipantResultEvent, + CallConnectionStateChangedEvent, + ToneInfo, + CallConnectionsAddParticipantResponse, + CallConnectionsPlayAudioResponse, + PhoneNumberIdentifierModel, + CommunicationIdentifierModel, + CommunicationUserIdentifierModel, + KnownToneValue, + KnownCallConnectionState, + KnownOperationStatus, + CallConnectionsCancelAllMediaOperationsResponse +} from "./generated/src/models"; /** Known values of {@link MediaType} that the service accepts. */ export const enum MediaType { @@ -14,7 +31,7 @@ export const enum MediaType { /** Known values of {@link EventSubscriptionType} that the service accepts. */ export const enum EventSubscriptionType { ParticipantsUpdated = "participantsUpdated", - DtmfReceived = "dtmfReceived" + ToneReceived = "toneReceived" } /** @@ -86,35 +103,56 @@ export type CancelAllMediaOperationsOptions = OperationOptions /** * Options to transfer call. */ - export type TransferCallOptions = OperationOptions +export type TransferCallOptions = OperationOptions + +/** + * Options to start recording. + */ + export type StartRecordingOptions = OperationOptions; + /** + * Options to pause recording. + */ + export type PauseRecordingOptions = OperationOptions; + /** + * Options to resume recording. + */ + export type ResumeRecordingOptions = OperationOptions; + /** + * Options to stop recording. + */ + export type StopRecordingOptions = OperationOptions; + /** + * Options to get recording properties. + */ + export type GetRecordingPropertiesOptions = OperationOptions; /** * Call Locator. */ export type CallLocator = -| GroupCallLocator -| ServerCallLocator; + | GroupCallLocator + | ServerCallLocator; - /** - * The group call locator. - */ +/** +* The group call locator. +*/ export interface GroupCallLocator { /** * The group call id. */ - groupCallId: string; + groupCallId: string; } - /** - * An Azure Communication user. - */ +/** +* An Azure Communication user. +*/ export interface ServerCallLocator { /** * The server call id. */ - serverCallId: string; + serverCallId: string; } /** @@ -122,7 +160,7 @@ export interface ServerCallLocator { * * @param locator - The assumed GroupCallLocator to be tested. */ - export const isGroupCallLocator = ( +export const isGroupCallLocator = ( locator: CallLocator ): locator is GroupCallLocator => { return typeof (locator as any).groupCallId === "string"; @@ -133,7 +171,7 @@ export interface ServerCallLocator { * * @param locator - The assumed ServerCallLocator to be tested. */ - export const isServerCallLocator = ( +export const isServerCallLocator = ( locator: CallLocator ): locator is ServerCallLocator => { return typeof (locator as any).serverCallId === "string"; @@ -142,9 +180,9 @@ export interface ServerCallLocator { /** * The CallLocatorKind is a discriminated union that adds a property `kind` to an CallLocator. */ - export type CallLocatorKind = - | GroupCallLocatorKind - | ServerCallLocatorKind; +export type CallLocatorKind = + | GroupCallLocatorKind + | ServerCallLocatorKind; /** * LocatorKind for a GroupCallLocator. @@ -182,3 +220,44 @@ export const getLocatorKind = ( } throw "unknow CallLocator type."; }; + +/** Defines values for CallingServerEventType. */ +enum CallingServerEventType { + + /** The call connection state change event type. */ + CALL_CONNECTION_STATE_CHANGED_EVENT = "Microsoft.Communication.CallConnectionStateChanged", + + /** The add participant result event type. */ + ADD_PARTICIPANT_RESULT_EVENT = "Microsoft.Communication.AddParticipantResult", + + /** The call recording state change event type. */ + CALL_RECORDING_STATE_CHANGED_EVENT = "Microsoft.Communication.CallRecordingStateChanged", + + /** The play audio result event type. */ + PLAY_AUDIO_RESULT_EVENT = "Microsoft.Communication.PlayAudioResult", + + /** The participants updated event type. */ + PARTICIPANTS_UPDATED_EVENT = "Microsoft.Communication.ParticipantsUpdated", + + /** The subscribe to tone event type. */ + TONE_RECEIVED_EVENT = "Microsoft.Communication.ToneReceived", +} + +export class KnownCallingServerEventType { + public static CALL_CONNECTION_STATE_CHANGED_EVENT: string | null = KnownCallingServerEventType.fromString("Microsoft.Communication.CallConnectionStateChanged") + public static ADD_PARTICIPANT_RESULT_EVENT: string | null = KnownCallingServerEventType.fromString("Microsoft.Communication.AddParticipantResult") + public static CALL_RECORDING_STATE_CHANGED_EVENT: string | null = KnownCallingServerEventType.fromString("Microsoft.Communication.CallRecordingStateChanged") + public static PLAY_AUDIO_RESULT_EVENT: string | null = KnownCallingServerEventType.fromString("Microsoft.Communication.PlayAudioResult") + public static PARTICIPANTS_UPDATED_EVENT: string | null = KnownCallingServerEventType.fromString("Microsoft.Communication.ParticipantsUpdated") + public static TONE_RECEIVED_EVENT: string | null = KnownCallingServerEventType.fromString("Microsoft.Communication.ToneReceived") + + public static fromString(value: string) { + var allEvents = Object.values(CallingServerEventType) + for (let entry of allEvents) { + if (entry.toString().toUpperCase() == value.toUpperCase()) { + return value; + } + } + return null; + } +} diff --git a/sdk/communication/communication-callingserver/src/utils/utils.ts b/sdk/communication/communication-callingserver/src/utils/utils.ts new file mode 100644 index 000000000000..104a55c2b49f --- /dev/null +++ b/sdk/communication/communication-callingserver/src/utils/utils.ts @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { URL } from "url"; + +export class CallingServerUtils { + public static isValidUrl(urlToVerify: string): boolean { + let url; + try { + url = new URL(urlToVerify); + } catch (_) { + return false; + } + + return url.protocol === "http:" || url.protocol === "https:"; + } +}