@webex/plugin-meetings 1.147.1 → 1.149.2
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 +17 -1
- 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 +79 -66
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +4 -4
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +1 -1
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/util.js +14 -0
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meetings/index.js +12 -13
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +0 -16
- package/dist/meetings/util.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 +10 -0
- package/src/meeting/index.js +156 -15
- package/src/meeting/request.js +91 -32
- package/src/meeting/util.js +4 -4
- package/src/meeting-info/index.js +4 -1
- package/src/meeting-info/util.js +13 -0
- package/src/meetings/index.js +10 -11
- package/src/meetings/util.js +0 -14
- package/src/metrics/index.js +5 -1
- package/test/integration/spec/journey.js +1 -4
- package/test/integration/spec/space-meeting.js +10 -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
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import uuid from 'uuid';
|
|
2
2
|
import {debounce} from 'lodash';
|
|
3
3
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
4
|
-
import {
|
|
5
|
-
deviceType
|
|
6
|
-
} from '@webex/common';
|
|
4
|
+
import {deviceType} from '@webex/common';
|
|
7
5
|
|
|
8
6
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
9
7
|
import {
|
|
@@ -20,6 +18,7 @@ import {
|
|
|
20
18
|
MEDIA,
|
|
21
19
|
PARTICIPANT,
|
|
22
20
|
PROVISIONAL_TYPE_DIAL_IN,
|
|
21
|
+
PROVISIONAL_TYPE_DIAL_OUT,
|
|
23
22
|
SEND_DTMF_ENDPOINT,
|
|
24
23
|
_SLIDES_
|
|
25
24
|
} from '../constants';
|
|
@@ -50,7 +49,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
50
49
|
*/
|
|
51
50
|
async joinMeeting(options) {
|
|
52
51
|
const {
|
|
53
|
-
asResourceOccupant,
|
|
52
|
+
asResourceOccupant,
|
|
53
|
+
sipUri,
|
|
54
|
+
meetingNumber,
|
|
55
|
+
deviceUrl,
|
|
56
|
+
locusUrl,
|
|
57
|
+
resourceId,
|
|
58
|
+
correlationId,
|
|
59
|
+
ensureConversation,
|
|
60
|
+
moderator,
|
|
61
|
+
pin,
|
|
62
|
+
moveToResource,
|
|
63
|
+
roapMessage,
|
|
64
|
+
preferTranscoding
|
|
54
65
|
} = options;
|
|
55
66
|
|
|
56
67
|
LoggerProxy.logger.info(
|
|
@@ -94,29 +105,16 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
94
105
|
if (locusUrl) {
|
|
95
106
|
url = `${locusUrl}/${PARTICIPANT}`;
|
|
96
107
|
}
|
|
97
|
-
else if (meetingNumber) {
|
|
98
|
-
try {
|
|
99
|
-
await this.webex.internal.services.waitForCatalog('postauth');
|
|
100
|
-
url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
|
|
101
|
-
body.invitee = {
|
|
102
|
-
address: `wbxmn:${meetingNumber}`
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
catch (e) {
|
|
106
|
-
LoggerProxy.logger.error(`Meeting:request#joinMeeting webex meeting id--> ${e}`);
|
|
107
|
-
throw (e);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else if (sipUri) {
|
|
108
|
+
else if (sipUri || meetingNumber) {
|
|
111
109
|
try {
|
|
112
110
|
await this.webex.internal.services.waitForCatalog('postauth');
|
|
113
111
|
url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
|
|
114
112
|
body.invitee = {
|
|
115
|
-
address: sipUri
|
|
113
|
+
address: sipUri || `wbxmn:${meetingNumber}`
|
|
116
114
|
};
|
|
117
115
|
}
|
|
118
116
|
catch (e) {
|
|
119
|
-
LoggerProxy.logger.error(`Meeting:request#joinMeeting
|
|
117
|
+
LoggerProxy.logger.error(`Meeting:request#joinMeeting ${sipUri ? 'sipUri' : 'meetingNumber'} --> ${e}`);
|
|
120
118
|
throw (e);
|
|
121
119
|
}
|
|
122
120
|
}
|
|
@@ -153,7 +151,10 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
153
151
|
* @private
|
|
154
152
|
*/
|
|
155
153
|
dialIn({
|
|
156
|
-
locusUrl,
|
|
154
|
+
locusUrl,
|
|
155
|
+
dialInUrl,
|
|
156
|
+
clientUrl,
|
|
157
|
+
correlationId
|
|
157
158
|
}) {
|
|
158
159
|
LoggerProxy.logger.info(
|
|
159
160
|
'Meeting:request#dialIn --> Provisioning a dial in device',
|
|
@@ -182,6 +183,52 @@ 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,
|
|
199
|
+
dialOutUrl,
|
|
200
|
+
phoneNumber,
|
|
201
|
+
clientUrl,
|
|
202
|
+
correlationId
|
|
203
|
+
}) {
|
|
204
|
+
LoggerProxy.logger.info(
|
|
205
|
+
'Meeting:request#dialOut --> Provisioning a dial out device',
|
|
206
|
+
correlationId
|
|
207
|
+
);
|
|
208
|
+
const uri = `${locusUrl}/${PARTICIPANT}`;
|
|
209
|
+
|
|
210
|
+
const body = {
|
|
211
|
+
device: {
|
|
212
|
+
deviceType: deviceType.PROVISIONAL,
|
|
213
|
+
provisionalType: PROVISIONAL_TYPE_DIAL_OUT,
|
|
214
|
+
url: dialOutUrl,
|
|
215
|
+
dialoutAddress: phoneNumber,
|
|
216
|
+
clientUrl
|
|
217
|
+
},
|
|
218
|
+
correlationId
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return this.request({
|
|
222
|
+
method: HTTP_VERBS.POST,
|
|
223
|
+
uri,
|
|
224
|
+
body
|
|
225
|
+
}).catch((err) => {
|
|
226
|
+
LoggerProxy.logger.error(`Meeting:request#dialOut --> Error provisioning a dial out device, error ${err}`);
|
|
227
|
+
|
|
228
|
+
throw err;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
185
232
|
/**
|
|
186
233
|
* Syns the missed delta event
|
|
187
234
|
* @param {Object} options
|
|
@@ -241,21 +288,23 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
241
288
|
}
|
|
242
289
|
|
|
243
290
|
/**
|
|
244
|
-
* Make a network request to make a provisioned
|
|
291
|
+
* Make a network request to make a provisioned phone leave the meeting
|
|
245
292
|
* @param {Object} options
|
|
246
293
|
* @param {String} options.locusUrl
|
|
247
|
-
* @param {String} options.
|
|
248
|
-
* @param {String} options.deviceUrl
|
|
249
|
-
* @param {String} options.resourceId,
|
|
294
|
+
* @param {String} options.phoneUrl
|
|
250
295
|
* @param {String} options.correlationId
|
|
296
|
+
* @param {String} options.selfId
|
|
251
297
|
* @returns {Promise}
|
|
252
298
|
* @private
|
|
253
299
|
*/
|
|
254
|
-
|
|
255
|
-
locusUrl,
|
|
300
|
+
disconnectPhoneAudio({
|
|
301
|
+
locusUrl,
|
|
302
|
+
phoneUrl,
|
|
303
|
+
correlationId,
|
|
304
|
+
selfId
|
|
256
305
|
}) {
|
|
257
306
|
LoggerProxy.logger.info(
|
|
258
|
-
`Meeting:request#
|
|
307
|
+
`Meeting:request#disconnectPhoneAudio --> request phone ${phoneUrl} to leave`,
|
|
259
308
|
correlationId
|
|
260
309
|
);
|
|
261
310
|
const uri = `${locusUrl}/${PARTICIPANT}/${selfId}/${LEAVE}`;
|
|
@@ -263,7 +312,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
263
312
|
const body = {
|
|
264
313
|
device: {
|
|
265
314
|
deviceType: deviceType.PROVISIONAL,
|
|
266
|
-
url:
|
|
315
|
+
url: phoneUrl
|
|
267
316
|
},
|
|
268
317
|
correlationId
|
|
269
318
|
};
|
|
@@ -273,7 +322,9 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
273
322
|
uri,
|
|
274
323
|
body
|
|
275
324
|
}).catch((err) => {
|
|
276
|
-
LoggerProxy.logger.error(
|
|
325
|
+
LoggerProxy.logger.error(
|
|
326
|
+
`Meeting:request#disconnectPhoneAudio --> Error when requesting phone ${phoneUrl} to leave, error ${err}`
|
|
327
|
+
);
|
|
277
328
|
|
|
278
329
|
throw err;
|
|
279
330
|
});
|
|
@@ -290,7 +341,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
290
341
|
* @returns {Promise}
|
|
291
342
|
*/
|
|
292
343
|
leaveMeeting({
|
|
293
|
-
locusUrl,
|
|
344
|
+
locusUrl,
|
|
345
|
+
selfId,
|
|
346
|
+
deviceUrl: url,
|
|
347
|
+
resourceId,
|
|
348
|
+
correlationId
|
|
294
349
|
}) {
|
|
295
350
|
LoggerProxy.logger.info(
|
|
296
351
|
'Meeting:request#leaveMeeting --> Leaving a meeting',
|
|
@@ -515,7 +570,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
515
570
|
* @returns {Promise}
|
|
516
571
|
*/
|
|
517
572
|
changeVideoLayout({
|
|
518
|
-
locusUrl,
|
|
573
|
+
locusUrl,
|
|
574
|
+
deviceUrl,
|
|
575
|
+
layoutType,
|
|
576
|
+
main,
|
|
577
|
+
content
|
|
519
578
|
}) {
|
|
520
579
|
// send main/content renderInfo only if both width and height are specified
|
|
521
580
|
if (main && (!main.width || !main.height)) {
|
package/src/meeting/util.js
CHANGED
|
@@ -134,7 +134,7 @@ MeetingUtil.cleanUp = (meeting) => {
|
|
|
134
134
|
.then(() => meeting.roap.stop(meeting.correlationId, meeting.roapSeq));
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
-
MeetingUtil.
|
|
137
|
+
MeetingUtil.disconnectPhoneAudio = (meeting, phoneUrl) => {
|
|
138
138
|
if (meeting.meetingState === FULL_STATE.INACTIVE) {
|
|
139
139
|
return Promise.reject(new MeetingNotActiveError());
|
|
140
140
|
}
|
|
@@ -143,11 +143,11 @@ MeetingUtil.leavePstn = (meeting, dialInUrl) => {
|
|
|
143
143
|
locusUrl: meeting.locusUrl,
|
|
144
144
|
selfId: meeting.selfId,
|
|
145
145
|
correlationId: meeting.correlationId,
|
|
146
|
-
|
|
146
|
+
phoneUrl
|
|
147
147
|
};
|
|
148
148
|
|
|
149
149
|
return meeting.meetingRequest
|
|
150
|
-
.
|
|
150
|
+
.disconnectPhoneAudio(options)
|
|
151
151
|
.then((response) => {
|
|
152
152
|
if (response?.body?.locus) {
|
|
153
153
|
meeting.locusInfo.onFullLocus(response.body.locus);
|
|
@@ -155,7 +155,7 @@ MeetingUtil.leavePstn = (meeting, dialInUrl) => {
|
|
|
155
155
|
})
|
|
156
156
|
.catch((err) => {
|
|
157
157
|
LoggerProxy.logger.error(
|
|
158
|
-
`Meeting:util#
|
|
158
|
+
`Meeting:util#disconnectPhoneAudio --> An error occured while disconnecting phone audio in meeting ${
|
|
159
159
|
meeting.id
|
|
160
160
|
}, error: ${err}`
|
|
161
161
|
);
|
|
@@ -108,7 +108,10 @@ export default class MeetingInfo {
|
|
|
108
108
|
* @memberof MeetingInfo
|
|
109
109
|
*/
|
|
110
110
|
fetchMeetingInfo(destination, type = null) {
|
|
111
|
-
return this.fetchInfoOptions(
|
|
111
|
+
return this.fetchInfoOptions(
|
|
112
|
+
MeetingInfoUtil.extractDestination(destination, type),
|
|
113
|
+
type
|
|
114
|
+
).then((options) =>
|
|
112
115
|
// fetch meeting info
|
|
113
116
|
this.requestFetchInfo(options).catch((error) => {
|
|
114
117
|
// if it failed the first time as meeting link
|
package/src/meeting-info/util.js
CHANGED
|
@@ -35,6 +35,19 @@ import {
|
|
|
35
35
|
|
|
36
36
|
const MeetingInfoUtil = {};
|
|
37
37
|
|
|
38
|
+
MeetingInfoUtil.extractDestination = (destination, type) => {
|
|
39
|
+
let dest = destination;
|
|
40
|
+
|
|
41
|
+
if (type === _LOCUS_ID_) {
|
|
42
|
+
if (!(destination && destination.url)) {
|
|
43
|
+
throw new ParameterError('You cannot create a meeting by locus without a locus.url defined');
|
|
44
|
+
}
|
|
45
|
+
dest = destination.url;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return dest;
|
|
49
|
+
};
|
|
50
|
+
|
|
38
51
|
MeetingInfoUtil.getParsedUrl = (link) => {
|
|
39
52
|
try {
|
|
40
53
|
let parsedUrl = url.parse(link);
|
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');
|
|
@@ -725,7 +724,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
725
724
|
this.meetingCollection.set(meeting);
|
|
726
725
|
|
|
727
726
|
try {
|
|
728
|
-
const info = await this.meetingInfo.fetchMeetingInfo(
|
|
727
|
+
const info = await this.meetingInfo.fetchMeetingInfo(destination, type);
|
|
729
728
|
|
|
730
729
|
meeting.parseMeetingInfo(info);
|
|
731
730
|
meeting.meetingInfo = info ? info.body : null;
|
package/src/meetings/util.js
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
CORRELATION_ID,
|
|
9
9
|
EVENT_TRIGGERS
|
|
10
10
|
} from '../constants';
|
|
11
|
-
import ParameterError from '../common/errors/parameter';
|
|
12
11
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
13
12
|
import Trigger from '../common/events/trigger-proxy';
|
|
14
13
|
|
|
@@ -32,19 +31,6 @@ import Trigger from '../common/events/trigger-proxy';
|
|
|
32
31
|
|
|
33
32
|
const MeetingsUtil = {};
|
|
34
33
|
|
|
35
|
-
MeetingsUtil.extractDestination = (destination, type) => {
|
|
36
|
-
let dest = destination;
|
|
37
|
-
|
|
38
|
-
if (type === _LOCUS_ID_) {
|
|
39
|
-
if (!(destination && destination.url)) {
|
|
40
|
-
throw new ParameterError('You cannot create a meeting by locus without a locus.url defined');
|
|
41
|
-
}
|
|
42
|
-
dest = destination.url;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return dest;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
34
|
MeetingsUtil.getMeetingAddedType = (type) => (type === _LOCUS_ID_ ? _INCOMING_ : _CREATED_);
|
|
49
35
|
|
|
50
36
|
MeetingsUtil.handleRoapMercury = (envelope, meetingCollection) => {
|
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
|
}
|
|
@@ -482,10 +482,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
482
482
|
.then((response) => {
|
|
483
483
|
assert.equal(response[0].result.memberId, alice.meeting.selfId);
|
|
484
484
|
}),
|
|
485
|
-
testUtils.waitForEvents([{scope: bob.meeting.members, event: 'members:update'}])
|
|
486
|
-
.then((response) => {
|
|
487
|
-
console.log('SCREEN SHARE RESPONSE ', JSON.stringify(response));
|
|
488
|
-
}),
|
|
485
|
+
testUtils.waitForEvents([{scope: bob.meeting.members, event: 'members:update'}]),
|
|
489
486
|
testUtils.waitForEvents([{scope: alice.meeting, event: 'media:ready'}])
|
|
490
487
|
.then((response) => {
|
|
491
488
|
console.log('MEDIA:READY event ', response[0].result);
|
|
@@ -44,6 +44,15 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
44
44
|
console.log('CONVERSATION', conversation);
|
|
45
45
|
space = conversation;
|
|
46
46
|
})
|
|
47
|
+
.then(async () => {
|
|
48
|
+
const destinationWithType = await alice.webex.meetings.meetingInfo.fetchMeetingInfo(space.url, 'CONVERSATION_URL');
|
|
49
|
+
const destinationNoType = await alice.webex.meetings.meetingInfo.fetchMeetingInfo(space.url);
|
|
50
|
+
|
|
51
|
+
assert.exists(destinationNoType);
|
|
52
|
+
assert.exists(destinationWithType);
|
|
53
|
+
assert.exists(destinationNoType.body.meetingNumber);
|
|
54
|
+
assert.exists(destinationWithType.body.meetingNumber);
|
|
55
|
+
})
|
|
47
56
|
.then(function aliceStartsMeeting() {
|
|
48
57
|
return Promise.all([
|
|
49
58
|
testUtils.delayedPromise(alice.webex.meetings.create(space.url)),
|
|
@@ -97,7 +106,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
97
106
|
|
|
98
107
|
|
|
99
108
|
it('alice adds x user as guest to space meeting', () => Promise.all([
|
|
100
|
-
testUtils.delayedPromise(guest.webex.meetings.create(alice.meeting.
|
|
109
|
+
testUtils.delayedPromise(guest.webex.meetings.create(alice.meeting.sipUri)),
|
|
101
110
|
testUtils.waitForEvents([{scope: guest.webex.meetings, event: 'meeting:added', user: guest}])
|
|
102
111
|
]).then(() =>
|
|
103
112
|
Promise.all([
|