@webex/plugin-meetings 1.147.0 → 1.149.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.js +18 -2
- package/dist/constants.js.map +1 -1
- package/dist/meeting/index.js +327 -181
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +72 -29
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +6 -15
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +11 -12
- package/dist/meetings/index.js.map +1 -1
- package/dist/metrics/index.js +6 -0
- package/dist/metrics/index.js.map +1 -1
- package/package.json +5 -5
- package/src/constants.js +11 -1
- package/src/meeting/index.js +156 -15
- package/src/meeting/request.js +53 -9
- package/src/meeting/util.js +5 -19
- package/src/meetings/index.js +9 -10
- package/src/metrics/index.js +5 -1
- package/test/unit/spec/meeting/index.js +65 -1
- package/test/unit/spec/meeting/request.js +30 -5
package/src/meeting/index.js
CHANGED
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
ONLINE,
|
|
58
58
|
OFFLINE,
|
|
59
59
|
PC_BAIL_TIMEOUT,
|
|
60
|
+
PSTN_STATUS,
|
|
60
61
|
QUALITY_LEVELS,
|
|
61
62
|
RECORDING_STATE,
|
|
62
63
|
ROAP_SEQ_PRE,
|
|
@@ -710,13 +711,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
710
711
|
*/
|
|
711
712
|
this.floorGrantPending = false;
|
|
712
713
|
/**
|
|
713
|
-
* The latest status of the dial in device (can be "JOINED", "CONNECTED", "LEFT"
|
|
714
|
+
* The latest status of the dial in device (can be "JOINED", "CONNECTED", "LEFT",
|
|
715
|
+
* "TRANSFERRING", "SUCCESS" or "")
|
|
714
716
|
* @instance
|
|
715
717
|
* @type {String}
|
|
716
718
|
* @private
|
|
717
719
|
* @memberof Meeting
|
|
718
720
|
*/
|
|
719
|
-
this.dialInDeviceStatus =
|
|
721
|
+
this.dialInDeviceStatus = PSTN_STATUS.UNKNOWN;
|
|
720
722
|
/**
|
|
721
723
|
* the url for provisioned device used to dial in
|
|
722
724
|
* @instance
|
|
@@ -725,6 +727,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
725
727
|
* @memberof Meeting
|
|
726
728
|
*/
|
|
727
729
|
this.dialInUrl = '';
|
|
730
|
+
/**
|
|
731
|
+
* The latest status of the dial out device (can be "JOINED", "CONNECTED", "LEFT",
|
|
732
|
+
* "TRANSFERRING", "SUCCESS" or "")
|
|
733
|
+
* @instance
|
|
734
|
+
* @type {String}
|
|
735
|
+
* @private
|
|
736
|
+
* @memberof Meeting
|
|
737
|
+
*/
|
|
738
|
+
this.dialOutDeviceStatus = PSTN_STATUS.UNKNOWN;
|
|
739
|
+
/**
|
|
740
|
+
* the url for provisioned device used to dial out
|
|
741
|
+
* @instance
|
|
742
|
+
* @type {String}
|
|
743
|
+
* @private
|
|
744
|
+
* @memberof Meeting
|
|
745
|
+
*/
|
|
746
|
+
this.dialOutUrl = '';
|
|
728
747
|
/**
|
|
729
748
|
* @instance
|
|
730
749
|
* @type {MediaMetrics}
|
|
@@ -1028,6 +1047,24 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1028
1047
|
};
|
|
1029
1048
|
}
|
|
1030
1049
|
|
|
1050
|
+
const joinRespTxStartAudio = this.getSendingMediaDelayDuration('audio');
|
|
1051
|
+
|
|
1052
|
+
if (joinRespTxStartAudio) {
|
|
1053
|
+
options.audioSetupDelay = {
|
|
1054
|
+
...options.audioSetupDelay,
|
|
1055
|
+
joinRespTxStart: joinRespTxStartAudio
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const joinRespTxStartVideo = this.getSendingMediaDelayDuration('video');
|
|
1060
|
+
|
|
1061
|
+
if (joinRespTxStartVideo) {
|
|
1062
|
+
options.videoSetupDelay = {
|
|
1063
|
+
...options.videoSetupDelay,
|
|
1064
|
+
joinRespTxStart: joinRespTxStartVideo
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1031
1068
|
if (options.type === MQA_STATS.CA_TYPE) {
|
|
1032
1069
|
payload = Metrics.initMediaPayload(options.event, identifiers, options);
|
|
1033
1070
|
}
|
|
@@ -1144,9 +1181,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1144
1181
|
pstnUpdate(payload) {
|
|
1145
1182
|
if (this.locusInfo.self) {
|
|
1146
1183
|
const dialInPstnDevice = payload.newSelf?.pstnDevices.find((device) => device.url === this.dialInUrl);
|
|
1184
|
+
const dialOutPstnDevice = payload.newSelf?.pstnDevices.find((device) => device.url === this.dialOutUrl);
|
|
1185
|
+
let changed = false;
|
|
1147
1186
|
|
|
1148
1187
|
if (dialInPstnDevice) {
|
|
1149
|
-
|
|
1188
|
+
const newStatus = dialInPstnDevice.dialingStatus ?? dialInPstnDevice.state;
|
|
1189
|
+
|
|
1190
|
+
if (newStatus !== this.dialInDeviceStatus) {
|
|
1191
|
+
this.dialInDeviceStatus = newStatus;
|
|
1192
|
+
changed = true;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if (dialOutPstnDevice) {
|
|
1197
|
+
const newStatus = dialOutPstnDevice.dialingStatus ?? dialOutPstnDevice.state;
|
|
1198
|
+
|
|
1199
|
+
if (newStatus !== this.dialOutDeviceStatus) {
|
|
1200
|
+
this.dialOutDeviceStatus = newStatus;
|
|
1201
|
+
changed = true;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
if (changed) {
|
|
1150
1206
|
Trigger.trigger(
|
|
1151
1207
|
this,
|
|
1152
1208
|
{
|
|
@@ -1157,7 +1213,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1157
1213
|
{
|
|
1158
1214
|
dialIn: {
|
|
1159
1215
|
status: this.dialInDeviceStatus,
|
|
1160
|
-
attendeeId: dialInPstnDevice
|
|
1216
|
+
attendeeId: dialInPstnDevice?.attendeeId
|
|
1217
|
+
},
|
|
1218
|
+
dialOut: {
|
|
1219
|
+
status: this.dialOutDeviceStatus,
|
|
1220
|
+
attendeeId: dialOutPstnDevice?.attendeeId
|
|
1161
1221
|
}
|
|
1162
1222
|
}
|
|
1163
1223
|
);
|
|
@@ -3313,7 +3373,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3313
3373
|
/**
|
|
3314
3374
|
* Use phone for meeting audio
|
|
3315
3375
|
* @param {String} phoneNumber If provided, it will dial-out using this number. If not provided, dial-in will be used
|
|
3316
|
-
* @returns {Promise} Resolves once the dial-in or dial-out request has completed
|
|
3376
|
+
* @returns {Promise} Resolves once the dial-in or dial-out request has completed, or rejects if it failed
|
|
3317
3377
|
* @public
|
|
3318
3378
|
* @memberof Meeting
|
|
3319
3379
|
*/
|
|
@@ -3322,17 +3382,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3322
3382
|
return this.dialInPstn();
|
|
3323
3383
|
}
|
|
3324
3384
|
|
|
3325
|
-
return
|
|
3385
|
+
return this.dialOutPstn(phoneNumber);
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
/**
|
|
3389
|
+
* Determines if the given pstnStatus is in a state which implies the phone is provisioned
|
|
3390
|
+
* @param {String} pstnStatus
|
|
3391
|
+
* @returns {Boolean}
|
|
3392
|
+
* @private
|
|
3393
|
+
* @memberof Meeting
|
|
3394
|
+
*/
|
|
3395
|
+
isPhoneProvisioned(pstnStatus) {
|
|
3396
|
+
return [PSTN_STATUS.JOINED, PSTN_STATUS.CONNECTED, PSTN_STATUS.SUCCESS].includes(pstnStatus);
|
|
3326
3397
|
}
|
|
3327
3398
|
|
|
3328
3399
|
/**
|
|
3329
3400
|
* Enable dial-in for audio
|
|
3330
|
-
* @returns {Promise} Resolves once the dial-in request has completed
|
|
3401
|
+
* @returns {Promise} Resolves once the dial-in request has completed, or rejects if it failed
|
|
3331
3402
|
* @private
|
|
3332
3403
|
* @memberof Meeting
|
|
3333
3404
|
*/
|
|
3334
3405
|
dialInPstn() {
|
|
3335
|
-
if (this.
|
|
3406
|
+
if (this.isPhoneProvisioned(this.dialInDeviceStatus)) return Promise.resolve(); // prevent multiple dial in devices from being provisioned
|
|
3336
3407
|
|
|
3337
3408
|
const {correlationId, locusUrl} = this;
|
|
3338
3409
|
|
|
@@ -3357,6 +3428,47 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3357
3428
|
stack: error.stack
|
|
3358
3429
|
}
|
|
3359
3430
|
);
|
|
3431
|
+
|
|
3432
|
+
return Promise.reject(error);
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
/**
|
|
3437
|
+
* Enable dial-out for audio
|
|
3438
|
+
* @param {String} phoneNumber Phone number to dial out to
|
|
3439
|
+
* @returns {Promise} Resolves once the dial-out request has completed (it doesn't wait for the user to answer the phone), or rejects if it failed
|
|
3440
|
+
* @private
|
|
3441
|
+
* @memberof Meeting
|
|
3442
|
+
*/
|
|
3443
|
+
dialOutPstn(phoneNumber) {
|
|
3444
|
+
if (this.isPhoneProvisioned(this.dialOutDeviceStatus)) return Promise.resolve(); // prevent multiple dial out devices from being provisioned
|
|
3445
|
+
|
|
3446
|
+
const {correlationId, locusUrl} = this;
|
|
3447
|
+
|
|
3448
|
+
if (!this.dialOutUrl) this.dialOutUrl = `dialout:///${uuid.v4()}`;
|
|
3449
|
+
|
|
3450
|
+
return this.meetingRequest.dialOut({
|
|
3451
|
+
correlationId,
|
|
3452
|
+
dialOutUrl: this.dialOutUrl,
|
|
3453
|
+
phoneNumber,
|
|
3454
|
+
locusUrl,
|
|
3455
|
+
clientUrl: this.deviceUrl
|
|
3456
|
+
}).then((res) => {
|
|
3457
|
+
this.locusInfo.onFullLocus(res.body.locus);
|
|
3458
|
+
}).catch((error) => {
|
|
3459
|
+
Metrics.sendOperationalMetric(
|
|
3460
|
+
METRICS_OPERATIONAL_MEASURES.ADD_DIAL_OUT_FAILURE,
|
|
3461
|
+
{
|
|
3462
|
+
correlation_id: this.correlationId,
|
|
3463
|
+
dial_out_url: this.dialOutUrl,
|
|
3464
|
+
locus_id: locusUrl.split('/').pop(),
|
|
3465
|
+
client_url: this.deviceUrl,
|
|
3466
|
+
reason: error.error?.message,
|
|
3467
|
+
stack: error.stack
|
|
3468
|
+
}
|
|
3469
|
+
);
|
|
3470
|
+
|
|
3471
|
+
return Promise.reject(error);
|
|
3360
3472
|
});
|
|
3361
3473
|
}
|
|
3362
3474
|
|
|
@@ -3368,13 +3480,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3368
3480
|
* @returns {Promise}
|
|
3369
3481
|
*/
|
|
3370
3482
|
disconnectPhoneAudio() {
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3483
|
+
return Promise.all([
|
|
3484
|
+
this.isPhoneProvisioned(this.dialInDeviceStatus) ?
|
|
3485
|
+
MeetingUtil.disconnectPhoneAudio(this, this.dialInUrl) :
|
|
3486
|
+
Promise.resolve(),
|
|
3487
|
+
this.isPhoneProvisioned(this.dialOutDeviceStatus) ?
|
|
3488
|
+
MeetingUtil.disconnectPhoneAudio(this, this.dialOutUrl) :
|
|
3489
|
+
Promise.resolve()
|
|
3490
|
+
]);
|
|
3378
3491
|
}
|
|
3379
3492
|
|
|
3380
3493
|
/**
|
|
@@ -4954,4 +5067,32 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4954
5067
|
|
|
4955
5068
|
return (start && end) ? end - start : undefined;
|
|
4956
5069
|
}
|
|
5070
|
+
|
|
5071
|
+
/**
|
|
5072
|
+
* @param {string} typeMedia 'audio' or 'video'
|
|
5073
|
+
* @returns {undefined}
|
|
5074
|
+
*/
|
|
5075
|
+
setStartSendingMediaDelay(typeMedia) {
|
|
5076
|
+
this[`startSendingMediaDelay${typeMedia}`] = performance.now();
|
|
5077
|
+
this[`endSendingMediaDelay${typeMedia}`] = undefined;
|
|
5078
|
+
}
|
|
5079
|
+
|
|
5080
|
+
/**
|
|
5081
|
+
* @param {string} typeMedia 'audio' or 'video'
|
|
5082
|
+
* @returns {undefined}
|
|
5083
|
+
*/
|
|
5084
|
+
setEndSendingMediaDelay(typeMedia) {
|
|
5085
|
+
this[`endSendingMediaDelay${typeMedia}`] = performance.now();
|
|
5086
|
+
}
|
|
5087
|
+
|
|
5088
|
+
/**
|
|
5089
|
+
* @param {string} typeMedia 'audio' or 'video'
|
|
5090
|
+
* @returns {string} duration between join response and first media tx
|
|
5091
|
+
*/
|
|
5092
|
+
getSendingMediaDelayDuration(typeMedia) {
|
|
5093
|
+
const start = this[`startSendingMediaDelay${typeMedia}`];
|
|
5094
|
+
const end = this[`endSendingMediaDelay${typeMedia}`];
|
|
5095
|
+
|
|
5096
|
+
return (start && end) ? end - start : undefined;
|
|
5097
|
+
}
|
|
4957
5098
|
}
|
package/src/meeting/request.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
MEDIA,
|
|
21
21
|
PARTICIPANT,
|
|
22
22
|
PROVISIONAL_TYPE_DIAL_IN,
|
|
23
|
+
PROVISIONAL_TYPE_DIAL_OUT,
|
|
23
24
|
SEND_DTMF_ENDPOINT,
|
|
24
25
|
_SLIDES_
|
|
25
26
|
} from '../constants';
|
|
@@ -182,6 +183,48 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
182
183
|
});
|
|
183
184
|
}
|
|
184
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Make a network request to add a dial out device
|
|
188
|
+
* @param {Object} options
|
|
189
|
+
* @param {String} options.correlationId
|
|
190
|
+
* @param {String} options.localUrl url for the meeting
|
|
191
|
+
* @param {String} options.dialOutUrl identifier for the to-be provisioned device
|
|
192
|
+
* @param {String} options.phoneNumber phone number to dial out to
|
|
193
|
+
* @param {String} options.clientUrl identifier for the web device
|
|
194
|
+
* @returns {Promise}
|
|
195
|
+
* @private
|
|
196
|
+
*/
|
|
197
|
+
dialOut({
|
|
198
|
+
locusUrl, dialOutUrl, phoneNumber, clientUrl, correlationId
|
|
199
|
+
}) {
|
|
200
|
+
LoggerProxy.logger.info(
|
|
201
|
+
'Meeting:request#dialOut --> Provisioning a dial out device',
|
|
202
|
+
correlationId
|
|
203
|
+
);
|
|
204
|
+
const uri = `${locusUrl}/${PARTICIPANT}`;
|
|
205
|
+
|
|
206
|
+
const body = {
|
|
207
|
+
device: {
|
|
208
|
+
deviceType: deviceType.PROVISIONAL,
|
|
209
|
+
provisionalType: PROVISIONAL_TYPE_DIAL_OUT,
|
|
210
|
+
url: dialOutUrl,
|
|
211
|
+
dialoutAddress: phoneNumber,
|
|
212
|
+
clientUrl
|
|
213
|
+
},
|
|
214
|
+
correlationId
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return this.request({
|
|
218
|
+
method: HTTP_VERBS.POST,
|
|
219
|
+
uri,
|
|
220
|
+
body
|
|
221
|
+
}).catch((err) => {
|
|
222
|
+
LoggerProxy.logger.error(`Meeting:request#dialOut --> Error provisioning a dial out device, error ${err}`);
|
|
223
|
+
|
|
224
|
+
throw err;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
185
228
|
/**
|
|
186
229
|
* Syns the missed delta event
|
|
187
230
|
* @param {Object} options
|
|
@@ -241,21 +284,20 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
241
284
|
}
|
|
242
285
|
|
|
243
286
|
/**
|
|
244
|
-
* Make a network request to make a provisioned
|
|
287
|
+
* Make a network request to make a provisioned phone leave the meeting
|
|
245
288
|
* @param {Object} options
|
|
246
289
|
* @param {String} options.locusUrl
|
|
247
|
-
* @param {String} options.
|
|
248
|
-
* @param {String} options.deviceUrl
|
|
249
|
-
* @param {String} options.resourceId,
|
|
290
|
+
* @param {String} options.phoneUrl
|
|
250
291
|
* @param {String} options.correlationId
|
|
292
|
+
* @param {String} options.selfId
|
|
251
293
|
* @returns {Promise}
|
|
252
294
|
* @private
|
|
253
295
|
*/
|
|
254
|
-
|
|
255
|
-
locusUrl,
|
|
296
|
+
disconnectPhoneAudio({
|
|
297
|
+
locusUrl, phoneUrl, correlationId, selfId
|
|
256
298
|
}) {
|
|
257
299
|
LoggerProxy.logger.info(
|
|
258
|
-
`Meeting:request#
|
|
300
|
+
`Meeting:request#disconnectPhoneAudio --> request phone ${phoneUrl} to leave`,
|
|
259
301
|
correlationId
|
|
260
302
|
);
|
|
261
303
|
const uri = `${locusUrl}/${PARTICIPANT}/${selfId}/${LEAVE}`;
|
|
@@ -263,7 +305,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
263
305
|
const body = {
|
|
264
306
|
device: {
|
|
265
307
|
deviceType: deviceType.PROVISIONAL,
|
|
266
|
-
url:
|
|
308
|
+
url: phoneUrl
|
|
267
309
|
},
|
|
268
310
|
correlationId
|
|
269
311
|
};
|
|
@@ -273,7 +315,9 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
273
315
|
uri,
|
|
274
316
|
body
|
|
275
317
|
}).catch((err) => {
|
|
276
|
-
LoggerProxy.logger.error(
|
|
318
|
+
LoggerProxy.logger.error(
|
|
319
|
+
`Meeting:request#disconnectPhoneAudio --> Error when requesting phone ${phoneUrl} to leave, error ${err}`
|
|
320
|
+
);
|
|
277
321
|
|
|
278
322
|
throw err;
|
|
279
323
|
});
|
package/src/meeting/util.js
CHANGED
|
@@ -72,13 +72,7 @@ MeetingUtil.hasOwner = (info) => info && info.owner;
|
|
|
72
72
|
|
|
73
73
|
MeetingUtil.isOwnerSelf = (owner, selfId) => owner === selfId;
|
|
74
74
|
|
|
75
|
-
MeetingUtil.isPinOrGuest = (err) =>
|
|
76
|
-
if (err && err.body && err.body.errorCode === INTENT_TO_JOIN) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
|
-
};
|
|
75
|
+
MeetingUtil.isPinOrGuest = (err) => err?.body?.errorCode && INTENT_TO_JOIN.includes(err.body.errorCode);
|
|
82
76
|
|
|
83
77
|
MeetingUtil.joinMeeting = (meeting, options) => {
|
|
84
78
|
if (!meeting) {
|
|
@@ -140,7 +134,7 @@ MeetingUtil.cleanUp = (meeting) => {
|
|
|
140
134
|
.then(() => meeting.roap.stop(meeting.correlationId, meeting.roapSeq));
|
|
141
135
|
};
|
|
142
136
|
|
|
143
|
-
MeetingUtil.
|
|
137
|
+
MeetingUtil.disconnectPhoneAudio = (meeting, phoneUrl) => {
|
|
144
138
|
if (meeting.meetingState === FULL_STATE.INACTIVE) {
|
|
145
139
|
return Promise.reject(new MeetingNotActiveError());
|
|
146
140
|
}
|
|
@@ -149,11 +143,11 @@ MeetingUtil.leavePstn = (meeting, dialInUrl) => {
|
|
|
149
143
|
locusUrl: meeting.locusUrl,
|
|
150
144
|
selfId: meeting.selfId,
|
|
151
145
|
correlationId: meeting.correlationId,
|
|
152
|
-
|
|
146
|
+
phoneUrl
|
|
153
147
|
};
|
|
154
148
|
|
|
155
149
|
return meeting.meetingRequest
|
|
156
|
-
.
|
|
150
|
+
.disconnectPhoneAudio(options)
|
|
157
151
|
.then((response) => {
|
|
158
152
|
if (response?.body?.locus) {
|
|
159
153
|
meeting.locusInfo.onFullLocus(response.body.locus);
|
|
@@ -161,7 +155,7 @@ MeetingUtil.leavePstn = (meeting, dialInUrl) => {
|
|
|
161
155
|
})
|
|
162
156
|
.catch((err) => {
|
|
163
157
|
LoggerProxy.logger.error(
|
|
164
|
-
`Meeting:util#
|
|
158
|
+
`Meeting:util#disconnectPhoneAudio --> An error occured while disconnecting phone audio in meeting ${
|
|
165
159
|
meeting.id
|
|
166
160
|
}, error: ${err}`
|
|
167
161
|
);
|
|
@@ -263,14 +257,6 @@ MeetingUtil.joinMeetingOptions = (meeting, options = {}) => {
|
|
|
263
257
|
.catch((err) => {
|
|
264
258
|
// joining a claimed PMR that is not my own, scenario B
|
|
265
259
|
if (MeetingUtil.isPinOrGuest(err)) {
|
|
266
|
-
if (MeetingUtil.hasOwner(meeting.meetingInfo)) {
|
|
267
|
-
return MeetingUtil.joinMeeting(meeting, options).then((response) => {
|
|
268
|
-
meeting.setLocus(response);
|
|
269
|
-
|
|
270
|
-
return Promise.resolve();
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
260
|
Metrics.postEvent({
|
|
275
261
|
event: eventType.PIN_PROMPT,
|
|
276
262
|
meeting
|
package/src/meetings/index.js
CHANGED
|
@@ -116,15 +116,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
116
116
|
*/
|
|
117
117
|
constructor(...args) {
|
|
118
118
|
super(...args);
|
|
119
|
-
/**
|
|
120
|
-
* The MeetingInfo object to interact with server
|
|
121
|
-
* @instance
|
|
122
|
-
* @type {Object}
|
|
123
|
-
* @private
|
|
124
|
-
* @memberof Meetings
|
|
125
|
-
*/
|
|
126
119
|
|
|
127
|
-
this.meetingInfo = null;
|
|
128
120
|
/**
|
|
129
121
|
* The Meetings request to interact with server
|
|
130
122
|
* @instance
|
|
@@ -381,6 +373,15 @@ export default class Meetings extends WebexPlugin {
|
|
|
381
373
|
LoggerConfig.set(this.config.logging);
|
|
382
374
|
LoggerProxy.set(this.webex.logger);
|
|
383
375
|
|
|
376
|
+
/**
|
|
377
|
+
* The MeetingInfo object to interact with server
|
|
378
|
+
* @instance
|
|
379
|
+
* @type {Object}
|
|
380
|
+
* @private
|
|
381
|
+
* @memberof Meetings
|
|
382
|
+
*/
|
|
383
|
+
this.meetingInfo = this.config.experimental.enableUnifiedMeetings ? new MeetingInfoV2(this.webex) : new MeetingInfo(this.webex);
|
|
384
|
+
|
|
384
385
|
Trigger.trigger(
|
|
385
386
|
this,
|
|
386
387
|
{
|
|
@@ -407,8 +408,6 @@ export default class Meetings extends WebexPlugin {
|
|
|
407
408
|
return Promise.reject(new Error('SDK cannot authorize'));
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
this.meetingInfo = this.config.experimental.enableUnifiedMeetings ? new MeetingInfoV2(this.webex) : new MeetingInfo(this.webex);
|
|
411
|
-
|
|
412
411
|
|
|
413
412
|
if (this.registered) {
|
|
414
413
|
LoggerProxy.logger.info('Meetings:index#register --> INFO, Meetings plugin already registered');
|
package/src/metrics/index.js
CHANGED
|
@@ -51,11 +51,15 @@ const triggerTimers = ({event, meeting, data}) => {
|
|
|
51
51
|
case eventType.LOCUS_JOIN_RESPONSE:
|
|
52
52
|
meeting.setStartSetupDelay(mediaType.AUDIO);
|
|
53
53
|
meeting.setStartSetupDelay(mediaType.VIDEO);
|
|
54
|
+
meeting.setStartSendingMediaDelay(mediaType.AUDIO);
|
|
55
|
+
meeting.setStartSendingMediaDelay(mediaType.VIDEO);
|
|
54
56
|
break;
|
|
55
57
|
case eventType.RECEIVING_MEDIA_START:
|
|
56
58
|
meeting.setEndSetupDelay(data.mediaType);
|
|
57
59
|
break;
|
|
58
|
-
|
|
60
|
+
case eventType.SENDING_MEDIA_START:
|
|
61
|
+
meeting.setEndSendingMediaDelay(data.mediaType);
|
|
62
|
+
break;
|
|
59
63
|
default:
|
|
60
64
|
break;
|
|
61
65
|
}
|
|
@@ -1902,7 +1902,9 @@ describe('plugin-meetings', () => {
|
|
|
1902
1902
|
|
|
1903
1903
|
describe('#usePhoneAudio', () => {
|
|
1904
1904
|
beforeEach(() => {
|
|
1905
|
-
meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.resolve());
|
|
1905
|
+
meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.resolve({body: {locus: 'testData'}}));
|
|
1906
|
+
meeting.meetingRequest.dialOut = sinon.stub().returns(Promise.resolve({body: {locus: 'testData'}}));
|
|
1907
|
+
meeting.locusInfo.onFullLocus = sinon.stub().returns(Promise.resolve());
|
|
1906
1908
|
});
|
|
1907
1909
|
|
|
1908
1910
|
it('with no parameters triggers dial-in, delegating request to meetingRequest correctly', async () => {
|
|
@@ -1915,8 +1917,11 @@ describe('plugin-meetings', () => {
|
|
|
1915
1917
|
locusUrl: meeting.locusUrl,
|
|
1916
1918
|
clientUrl: meeting.deviceUrl
|
|
1917
1919
|
});
|
|
1920
|
+
assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
|
|
1921
|
+
assert.notCalled(meeting.meetingRequest.dialOut);
|
|
1918
1922
|
|
|
1919
1923
|
meeting.meetingRequest.dialIn.resetHistory();
|
|
1924
|
+
meeting.locusInfo.onFullLocus.resetHistory();
|
|
1920
1925
|
|
|
1921
1926
|
// try again. the dial in urls should match
|
|
1922
1927
|
await meeting.usePhoneAudio();
|
|
@@ -1927,6 +1932,65 @@ describe('plugin-meetings', () => {
|
|
|
1927
1932
|
locusUrl: meeting.locusUrl,
|
|
1928
1933
|
clientUrl: meeting.deviceUrl
|
|
1929
1934
|
});
|
|
1935
|
+
assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
|
|
1936
|
+
assert.notCalled(meeting.meetingRequest.dialOut);
|
|
1937
|
+
});
|
|
1938
|
+
|
|
1939
|
+
it('given a phone number, triggers dial-out, delegating request to meetingRequest correctly', async () => {
|
|
1940
|
+
const phoneNumber = '+442088241000';
|
|
1941
|
+
|
|
1942
|
+
await meeting.usePhoneAudio(phoneNumber);
|
|
1943
|
+
const DIAL_OUT_URL = meeting.dialOutUrl;
|
|
1944
|
+
|
|
1945
|
+
assert.calledWith(meeting.meetingRequest.dialOut, {
|
|
1946
|
+
correlationId: meeting.correlationId,
|
|
1947
|
+
dialOutUrl: DIAL_OUT_URL,
|
|
1948
|
+
locusUrl: meeting.locusUrl,
|
|
1949
|
+
clientUrl: meeting.deviceUrl,
|
|
1950
|
+
phoneNumber
|
|
1951
|
+
});
|
|
1952
|
+
assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
|
|
1953
|
+
assert.notCalled(meeting.meetingRequest.dialIn);
|
|
1954
|
+
|
|
1955
|
+
meeting.meetingRequest.dialOut.resetHistory();
|
|
1956
|
+
meeting.locusInfo.onFullLocus.resetHistory();
|
|
1957
|
+
|
|
1958
|
+
// try again. the dial out urls should match
|
|
1959
|
+
await meeting.usePhoneAudio(phoneNumber);
|
|
1960
|
+
|
|
1961
|
+
assert.calledWith(meeting.meetingRequest.dialOut, {
|
|
1962
|
+
correlationId: meeting.correlationId,
|
|
1963
|
+
dialOutUrl: DIAL_OUT_URL,
|
|
1964
|
+
locusUrl: meeting.locusUrl,
|
|
1965
|
+
clientUrl: meeting.deviceUrl,
|
|
1966
|
+
phoneNumber
|
|
1967
|
+
});
|
|
1968
|
+
assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
|
|
1969
|
+
assert.notCalled(meeting.meetingRequest.dialIn);
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
it('rejects if the request failed (dial in)', () => {
|
|
1973
|
+
const error = 'something bad happened';
|
|
1974
|
+
|
|
1975
|
+
meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.reject(error));
|
|
1976
|
+
|
|
1977
|
+
return meeting.usePhoneAudio().then(() => Promise.reject(new Error('Promise resolved when it should have rejected'))).catch((e) => {
|
|
1978
|
+
assert.equal(e, error);
|
|
1979
|
+
|
|
1980
|
+
return Promise.resolve();
|
|
1981
|
+
});
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
it('rejects if the request failed (dial out)', async () => {
|
|
1985
|
+
const error = 'something bad happened';
|
|
1986
|
+
|
|
1987
|
+
meeting.meetingRequest.dialOut = sinon.stub().returns(Promise.reject(error));
|
|
1988
|
+
|
|
1989
|
+
return meeting.usePhoneAudio('+441234567890').then(() => Promise.reject(new Error('Promise resolved when it should have rejected'))).catch((e) => {
|
|
1990
|
+
assert.equal(e, error);
|
|
1991
|
+
|
|
1992
|
+
return Promise.resolve();
|
|
1993
|
+
});
|
|
1930
1994
|
});
|
|
1931
1995
|
});
|
|
1932
1996
|
|
|
@@ -152,23 +152,48 @@ describe('plugin-meetings', () => {
|
|
|
152
152
|
assert.equal(requestParams.body.device.clientUrl, 'clientUrl');
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
it('sends
|
|
155
|
+
it('sends dial out pstn request', async () => {
|
|
156
|
+
const locusUrl = 'locusUrl';
|
|
157
|
+
const clientUrl = 'clientUrl';
|
|
158
|
+
const correlationId = 'random-uuid';
|
|
159
|
+
const dialOutUrl = 'url';
|
|
160
|
+
const phoneNumber = '+442088241000';
|
|
161
|
+
|
|
162
|
+
await meetingsRequest.dialOut({
|
|
163
|
+
locusUrl,
|
|
164
|
+
clientUrl,
|
|
165
|
+
correlationId,
|
|
166
|
+
dialOutUrl,
|
|
167
|
+
phoneNumber
|
|
168
|
+
});
|
|
169
|
+
const requestParams = meetingsRequest.request.getCall(0).args[0];
|
|
170
|
+
|
|
171
|
+
assert.equal(requestParams.method, 'POST');
|
|
172
|
+
assert.equal(requestParams.uri, `${locusUrl}/participant`);
|
|
173
|
+
assert.equal(requestParams.body.device.url, dialOutUrl);
|
|
174
|
+
assert.equal(requestParams.body.device.deviceType, 'PROVISIONAL');
|
|
175
|
+
assert.equal(requestParams.body.device.provisionalType, 'DIAL_OUT');
|
|
176
|
+
assert.equal(requestParams.body.device.clientUrl, 'clientUrl');
|
|
177
|
+
assert.equal(requestParams.body.device.dialoutAddress, phoneNumber);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('sends disconnect phone audio request', async () => {
|
|
156
181
|
const locusUrl = 'locusUrl';
|
|
157
182
|
const selfId = 'selfId';
|
|
158
183
|
const correlationId = 'random-uuid';
|
|
159
|
-
const
|
|
184
|
+
const phoneUrl = 'url';
|
|
160
185
|
|
|
161
|
-
await meetingsRequest.
|
|
186
|
+
await meetingsRequest.disconnectPhoneAudio({
|
|
162
187
|
locusUrl,
|
|
163
188
|
selfId,
|
|
164
189
|
correlationId,
|
|
165
|
-
|
|
190
|
+
phoneUrl
|
|
166
191
|
});
|
|
167
192
|
const requestParams = meetingsRequest.request.getCall(0).args[0];
|
|
168
193
|
|
|
169
194
|
assert.equal(requestParams.method, 'PUT');
|
|
170
195
|
assert.equal(requestParams.uri, `${locusUrl}/participant/${selfId}/leave`);
|
|
171
|
-
assert.equal(requestParams.body.device.url,
|
|
196
|
+
assert.equal(requestParams.body.device.url, phoneUrl);
|
|
172
197
|
assert.equal(requestParams.body.device.deviceType, 'PROVISIONAL');
|
|
173
198
|
});
|
|
174
199
|
});
|