Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,33 @@ export const isSdpOfferCreationError = (rawError: any) => {
return false;
};

/**
* Checks if the given error is a browser media error by its name.
* Returns true if the error name matches any known browser media error name in the mapping.
*
* @param {Object} rawError - The error object to check.
* @returns {boolean} True if the error is a browser media error, false otherwise.
*/
export const isBrowserMediaError = (rawError) => {
// eslint-disable-next-line no-use-before-define
if (isBrowserMediaErrorName(rawError.name)) {
return true;
}

return false;
};

/**
* Returns the client error code mapped to the given browser media error name.
* If the error name is not found in the mapping, returns undefined.
*
* @param {Object} rawError - The error object containing the error name.
* @returns {string|undefined} The mapped client error code, or undefined if not found.
*/
export const getBrowserMediaErrorCode = (rawError) => {
return BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP[rawError.name];
};

/**
* MDN Media Devices getUserMedia() method returns a name if it errs
* Documentation can be found here: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ICE_AND_REACHABILITY_FAILED_CLIENT_CODE,
ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE,
MISSING_ROAP_ANSWER_CLIENT_CODE,
BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP,
} from '../../../../src/call-diagnostic/config';
import Logger from '@webex/plugin-logger';

Expand Down Expand Up @@ -685,4 +686,34 @@ describe('internal-plugin-metrics', () => {
});
});
});

describe('isBrowserMediaError', () => {
it('should return true if error name is in BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP', () => {
// Use a known browser media error name from the config map
const errorName = Object.keys(BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP)[0];
const error = {name: errorName};
assert.isTrue(CallDiagnosticUtils.isBrowserMediaError(error));
});

it('should return false if error name is not in BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP', () => {
const error = {name: 'SomeOtherError'};
assert.isFalse(CallDiagnosticUtils.isBrowserMediaError(error));
});
});

describe('getBrowserMediaErrorCode', () => {
it('should return correct error code for known error name', () => {
const errorName = Object.keys(BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP)[0];
const error = {name: errorName};
assert.strictEqual(
CallDiagnosticUtils.getBrowserMediaErrorCode(error),
BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP[errorName]
);
});

it('should return undefined for unknown error name', () => {
const error = {name: 'UnknownError'};
assert.isUndefined(CallDiagnosticUtils.getBrowserMediaErrorCode(error));
});
});
});
21 changes: 20 additions & 1 deletion packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import uuid from 'uuid';
import {cloneDeep, isEqual, isEmpty} from 'lodash';
import {cloneDeep, isEqual, isEmpty, merge} from 'lodash';
import jwtDecode from 'jwt-decode';
// @ts-ignore - Fix this
import {StatelessWebexPlugin} from '@webex/webex-core';
Expand Down Expand Up @@ -50,6 +50,11 @@ import {
type MeetingTranscriptPayload,
} from '@webex/internal-plugin-voicea';

import {
getBrowserMediaErrorCode,
isBrowserMediaError,
isBrowserMediaErrorName,
} from '@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util';
import {processNewCaptions} from './voicea-meeting';

import {
Expand Down Expand Up @@ -5440,6 +5445,20 @@ export default class Meeting extends StatelessWebexPlugin {
shouldRetry = false;
}

if (CallDiagnosticUtils.isBrowserMediaError(error)) {
shouldRetry = false;
// eslint-disable-next-line no-ex-assign
error = merge({
error: {
body: {
errorCode: CallDiagnosticUtils.getBrowserMediaErrorCode(error),
message: error?.message,
name: error?.name,
},
},
});
}

// we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
if (joined && (isRetry || !shouldRetry)) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ describe('plugin-meetings', () => {
});
});

describe('#joinWithMedia', () => {
describe.only('#joinWithMedia', () => {
it('should have #joinWithMedia', () => {
assert.exists(meeting.joinWithMedia);
});
Expand Down Expand Up @@ -1003,6 +1003,35 @@ describe('plugin-meetings', () => {
);
});

it('should call leave() if addMediaInternal() fails ', async () => {
const addMediaError = new Error('fake addMedia error');
addMediaError.name = 'TypeError';

const rejectError = {
error: {
body: {
errorCode: 2729,
message: 'fake addMedia error',
name: 'TypeError'
}
}
};
meeting.addMediaInternal.rejects(addMediaError);
sinon.stub(meeting, 'leave').resolves();

await assert.isRejected(
meeting.joinWithMedia({
joinOptions,
mediaOptions,
}),
rejectError
);

assert.calledOnce(meeting.join);
assert.calledOnce(meeting.addMediaInternal);
assert.calledOnce(Metrics.sendBehavioralMetric);
});

it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
const addMediaError = new Error('fake addMedia error');
const leaveStub = sinon.stub(meeting, 'leave');
Expand Down
Loading