diff --git a/change-beta/@azure-communication-react-3d93404e-3923-46bb-8c8a-e8854a8860db.json b/change-beta/@azure-communication-react-3d93404e-3923-46bb-8c8a-e8854a8860db.json new file mode 100644 index 00000000000..5898c4f6cd6 --- /dev/null +++ b/change-beta/@azure-communication-react-3d93404e-3923-46bb-8c8a-e8854a8860db.json @@ -0,0 +1,9 @@ +{ + "type": "minor", + "area": "fix", + "workstream": "Error handling", + "comment": "Add code and subcode to calling errors to help with debugging", + "packageName": "@azure/communication-react", + "email": "dmceachern@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-communication-react-3d93404e-3923-46bb-8c8a-e8854a8860db.json b/change/@azure-communication-react-3d93404e-3923-46bb-8c8a-e8854a8860db.json new file mode 100644 index 00000000000..5898c4f6cd6 --- /dev/null +++ b/change/@azure-communication-react-3d93404e-3923-46bb-8c8a-e8854a8860db.json @@ -0,0 +1,9 @@ +{ + "type": "minor", + "area": "fix", + "workstream": "Error handling", + "comment": "Add code and subcode to calling errors to help with debugging", + "packageName": "@azure/communication-react", + "email": "dmceachern@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index 398f4b24f18..b0f17162905 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -16,7 +16,8 @@ import { ParticipantRole, RemoteParticipantState as RemoteParticipantStatus, ScalingMode, - VideoDeviceInfo + VideoDeviceInfo, + CommunicationServicesError } from '@azure/communication-calling'; /* @conditional-compile-remove(breakout-rooms) */ import { BreakoutRoom, BreakoutRoomsSettings } from '@azure/communication-calling'; @@ -1118,15 +1119,30 @@ export class CallError extends Error { * Timestamp added to the error by the stateful layer. */ public timestamp: Date; + /** + * Primary code for the calling error + */ + public code?: number; + /** + * Sub code for the calling error + */ + public subCode?: number; + /** needs to be a (innerError as CommunicationServicesError) */ constructor(target: CallErrorTarget, innerError: Error, timestamp?: Date) { super(); this.target = target; this.innerError = innerError; + if ('code' in (innerError as CommunicationServicesError)) { + this.code = (innerError as CommunicationServicesError).code; + } + if ('subCode' in (innerError as CommunicationServicesError)) { + this.subCode = (innerError as CommunicationServicesError).subCode; + } // Testing note: It is easier to mock Date::now() than the Date() constructor. this.timestamp = timestamp ?? new Date(Date.now()); this.name = 'CallError'; - this.message = `${this.target}: ${this.innerError.message}`; + this.message = `${this.target}: ${this.innerError.message} code=${this.code} subCode=${this.subCode}`; } } diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 1e39056157c..1bb6fe51d21 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -424,6 +424,8 @@ export interface CallAdapter extends CommonCallAdapter { // @public export type CallAdapterCallEndedEvent = { callId: string; + code?: number; + subCode?: number; }; // @public @deprecated @@ -1085,7 +1087,9 @@ export type CallEndedListener = (event: CallAdapterCallEndedEvent) => void; // @public export class CallError extends Error { constructor(target: CallErrorTarget, innerError: Error, timestamp?: Date); + code?: number; innerError: Error; + subCode?: number; target: CallErrorTarget; timestamp: Date; } diff --git a/packages/communication-react/review/stable/communication-react.api.md b/packages/communication-react/review/stable/communication-react.api.md index 60b425e93cc..7673063b760 100644 --- a/packages/communication-react/review/stable/communication-react.api.md +++ b/packages/communication-react/review/stable/communication-react.api.md @@ -278,6 +278,8 @@ export interface CallAdapter extends CommonCallAdapter { // @public export type CallAdapterCallEndedEvent = { callId: string; + code?: number; + subCode?: number; }; // @public @deprecated @@ -899,7 +901,9 @@ export type CallEndedListener = (event: CallAdapterCallEndedEvent) => void; // @public export class CallError extends Error { constructor(target: CallErrorTarget, innerError: Error, timestamp?: Date); + code?: number; innerError: Error; + subCode?: number; target: CallErrorTarget; timestamp: Date; } diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts index 51f994a8c31..81e8141f329 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts @@ -312,7 +312,18 @@ class CallContext { /* @conditional-compile-remove(unsupported-browser) */ environmentInfo ); if (!IsCallEndedPage(oldPage) && IsCallEndedPage(newPage)) { - this.emitter.emit('callEnded', { callId: this.callId }); + /** + * We want to make sure that the id of the call that is ending + * is the same as the call in the adapter as this is a scenario where + * the call has ended and not been transferred and report the codes for this call. + */ + if (this.callId === latestEndedCall?.id) { + this.emitter.emit('callEnded', { + callId: latestEndedCall?.id, + code: latestEndedCall?.callEndReason?.code, + subCode: latestEndedCall?.callEndReason?.subCode + }); + } // Reset the callId to undefined as the call has ended. this.setCurrentCallId(undefined); // Make sure that the call is set to undefined in the state. diff --git a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts index ff5fcc74e24..4cc07e6c512 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts @@ -257,7 +257,7 @@ export type DisplayNameChangedListener = (event: { * * @public */ -export type CallAdapterCallEndedEvent = { callId: string }; +export type CallAdapterCallEndedEvent = { callId: string; code?: number; subCode?: number }; /** * Callback for {@link CallAdapterSubscribers} 'callEnded' event. diff --git a/samples/Calling/src/app/views/CallScreen.tsx b/samples/Calling/src/app/views/CallScreen.tsx index 36732474b45..581c6cd0006 100644 --- a/samples/Calling/src/app/views/CallScreen.tsx +++ b/samples/Calling/src/app/views/CallScreen.tsx @@ -54,8 +54,11 @@ export const CallScreen = (props: CallScreenProps): JSX.Element => { console.log(`Call Id: ${callIdRef.current}`); } }); - adapter.on('transferAccepted', (e) => { - console.log('Call being transferred to: ' + e); + adapter.on('transferAccepted', (event) => { + console.log('Call being transferred to: ' + event); + }); + adapter.on('callEnded', (event) => { + console.log('Call ended id: ' + event.callId + ' code: ' + event.code, 'subcode: ' + event.subCode); }); }, []);