diff --git a/packages/@webex/plugin-meetings/src/constants.ts b/packages/@webex/plugin-meetings/src/constants.ts index d3223f8a56a..4ca1117ec3c 100644 --- a/packages/@webex/plugin-meetings/src/constants.ts +++ b/packages/@webex/plugin-meetings/src/constants.ts @@ -203,6 +203,7 @@ export const ICE_AND_DTLS_CONNECTION_TIMEOUT = 20000; export const ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT = 35000; export const WEBINAR_ERROR_WEBCAST = [403026]; export const WEBINAR_ERROR_REGISTRATIONID = [403037, 403137]; +export const JOIN_ERROR_REASON_FOR_END_USER = [403003]; // ******************** REGEX ********************** // Please alphabetize @@ -1333,6 +1334,7 @@ export const MEETING_INFO_FAILURE_REASON = { WEBINAR_REGISTRATION: 'WEBINAR_REGISTRATION', // webinar need registration NEED_JOIN_WITH_WEBCAST: 'NEED_JOIN_WITH_WEBCAST', // webinar need using webcast join WEBINAR_NEED_REGISTRATIONID: 'WEBINAR_NEED_REGISTRATIONID', // webinar need registrationID + NOT_REACH_JBH: 'NOT_REACH_JBH', // Meeting is not allow to access since not reach JBH time OTHER: 'OTHER', // any other error (network, etc) }; diff --git a/packages/@webex/plugin-meetings/src/meeting/index.ts b/packages/@webex/plugin-meetings/src/meeting/index.ts index 46e2911c7d1..610a295cd61 100644 --- a/packages/@webex/plugin-meetings/src/meeting/index.ts +++ b/packages/@webex/plugin-meetings/src/meeting/index.ts @@ -123,6 +123,7 @@ import { NAMED_MEDIA_GROUP_TYPE_AUDIO, WEBINAR_ERROR_WEBCAST, WEBINAR_ERROR_REGISTRATIONID, + JOIN_ERROR_REASON_FOR_END_USER, } from '../constants'; import BEHAVIORAL_METRICS from '../metrics/constants'; import ParameterError from '../common/errors/parameter'; @@ -1790,6 +1791,18 @@ export default class Meeting extends StatelessWebexPlugin { `Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - password required (code=${err?.body?.code}).` ); + // Handle the case where user hasn't reached Join Before Host (JBH) time (error code 403003) + if (JOIN_ERROR_REASON_FOR_END_USER.includes(err.wbxAppApiCode)) { + this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH; + this.meetingInfoFailureCode = err.wbxAppApiCode; + + if (err.meetingInfo) { + this.meetingInfo = err.meetingInfo; + } + + throw new JoinWebinarError(); + } + // when wbxappapi requires password it still populates partial meeting info in the response if (err.meetingInfo) { this.meetingInfo = err.meetingInfo; diff --git a/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js b/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js index 431f6f01ee8..3a8d4cbaca2 100644 --- a/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js +++ b/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js @@ -6339,6 +6339,38 @@ describe('plugin-meetings', () => { assert.equal(meeting.passwordStatus, PASSWORD_STATUS.REQUIRED); }); + it('handles meetingInfoProvider not reach JBH', async () => { + meeting.destination = FAKE_DESTINATION; + meeting.destinationType = FAKE_TYPE; + meeting.attrs.meetingInfoProvider = { + fetchMeetingInfo: sinon + .stub() + .throws(new MeetingInfoV2PasswordError(403003, FAKE_MEETING_INFO)), + }; + + await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError); + + assert.calledWith( + meeting.attrs.meetingInfoProvider.fetchMeetingInfo, + FAKE_DESTINATION, + FAKE_TYPE, + null, + null, + undefined, + 'locus-id', + {}, + {meetingId: meeting.id, sendCAevents: true} + ); + + assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO); + assert.equal(meeting.meetingInfoFailureCode, 403003); + assert.equal( + meeting.meetingInfoFailureReason, + MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH + ); + assert.equal(meeting.requiredCaptcha, null); + }); + it('handles meetingInfoProvider policy error', async () => { meeting.destination = FAKE_DESTINATION; meeting.destinationType = FAKE_TYPE;