@webex/plugin-meetings 3.8.0-next.5 → 3.8.0-next.6

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.
@@ -209,7 +209,7 @@ var Breakout = _webexCore.WebexPlugin.extend({
209
209
  sessionId: this.sessionId
210
210
  });
211
211
  },
212
- version: "3.8.0-next.5"
212
+ version: "3.8.0-next.6"
213
213
  });
214
214
  var _default = exports.default = Breakout;
215
215
  //# sourceMappingURL=breakout.js.map
@@ -1046,7 +1046,7 @@ var Breakouts = _webexCore.WebexPlugin.extend({
1046
1046
  this.trigger(_constants.BREAKOUTS.EVENTS.ASK_RETURN_TO_MAIN);
1047
1047
  }
1048
1048
  },
1049
- version: "3.8.0-next.5"
1049
+ version: "3.8.0-next.6"
1050
1050
  });
1051
1051
  var _default = exports.default = Breakouts;
1052
1052
  //# sourceMappingURL=index.js.map
@@ -373,7 +373,7 @@ var SimultaneousInterpretation = _webexCore.WebexPlugin.extend({
373
373
  throw error;
374
374
  });
375
375
  },
376
- version: "3.8.0-next.5"
376
+ version: "3.8.0-next.6"
377
377
  });
378
378
  var _default = exports.default = SimultaneousInterpretation;
379
379
  //# sourceMappingURL=index.js.map
@@ -18,7 +18,7 @@ var SILanguage = _webexCore.WebexPlugin.extend({
18
18
  languageCode: 'number',
19
19
  languageName: 'string'
20
20
  },
21
- version: "3.8.0-next.5"
21
+ version: "3.8.0-next.6"
22
22
  });
23
23
  var _default = exports.default = SILanguage;
24
24
  //# sourceMappingURL=siLanguage.js.map
@@ -19,6 +19,7 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpe
19
19
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
20
20
  var _lodash = require("lodash");
21
21
  var _common = require("@webex/common");
22
+ var _events = require("events");
22
23
  var _webexCore = require("@webex/webex-core");
23
24
  var _constants = require("../constants");
24
25
  var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy"));
@@ -236,13 +237,18 @@ var LocusMediaRequest = exports.LocusMediaRequest = /*#__PURE__*/function (_Webe
236
237
  if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {
237
238
  this.confluenceState = 'creation in progress';
238
239
  }
239
-
240
- // @ts-ignore
241
- return this.request({
240
+ var upload = new _events.EventEmitter();
241
+ var download = new _events.EventEmitter();
242
+ var options = {
242
243
  method: _constants.HTTP_VERBS.PUT,
243
244
  uri: uri,
244
- body: body
245
- }).then(function (result) {
245
+ body: body,
246
+ upload: upload,
247
+ download: download
248
+ };
249
+
250
+ // @ts-ignore
251
+ var promise = this.request(options).then(function (result) {
246
252
  if (isRequestAffectingConfluenceState(request)) {
247
253
  _this4.confluenceState = 'created';
248
254
  }
@@ -272,6 +278,16 @@ var LocusMediaRequest = exports.LocusMediaRequest = /*#__PURE__*/function (_Webe
272
278
  }
273
279
  throw e;
274
280
  });
281
+ if (request.type === 'RoapMessage') {
282
+ var setupProgressListener = function setupProgressListener(direction, eventEmitter) {
283
+ eventEmitter.on('progress', function (progressEvent) {
284
+ _loggerProxy.default.logger.info("".concat(request.type, ": ").concat(direction, " Progress, Timestamp: ").concat(progressEvent.timeStamp, ", Progress: ").concat(progressEvent.loaded, "/").concat(progressEvent.total));
285
+ });
286
+ };
287
+ setupProgressListener('Upload', options.upload);
288
+ setupProgressListener('Download', options.download);
289
+ }
290
+ return promise;
275
291
  }
276
292
 
277
293
  /**
@@ -1 +1 @@
1
- {"version":3,"names":["_lodash","require","_common","_webexCore","_constants","_loggerProxy","_interopRequireDefault","_createSuper","Derived","hasNativeReflectConstruct","_isNativeReflectConstruct","_createSuperInternal","Super","_getPrototypeOf2","default","result","NewTarget","constructor","_Reflect$construct","arguments","apply","_possibleConstructorReturn2","Reflect","sham","Proxy","Boolean","prototype","valueOf","call","e","InternalRequestInfo","request","pendingPromise","sendRequestFn","_classCallCheck2","_defineProperty2","pendingPromises","_createClass2","key","value","getPendingPromises","addPendingPromises","_this$pendingPromises","push","_toConsumableArray2","execute","_this","then","forEach","d","resolve","catch","reject","isRequestAffectingConfluenceState","type","roapMessage","messageType","ROAP","ROAP_TYPES","OFFER","LocusMediaRequest","exports","_WebexPlugin","_inherits2","_super","config","options","_this2","_assertThisInitialized2","isRequestInProgress","queuedRequests","confluenceState","addToQueue","info","length","filter","r","executeNextQueuedRequest","_this3","nextRequest","shift","getLatestMuteState","audioMuted","latestAudioMuted","undefined","videoMuted","latestVideoMuted","sendHttpRequest","_this4","uri","concat","selfUrl","MEDIA","_this$getLatestMuteSt","body","device","correlationId","localMedias","respOnlySdp","usingResource","reachability","clientMediaPreferences","webex","internal","newMetrics","submitClientEvent","name","meetingId","sequence","localSdp","_stringify","mediaId","LoggerProxy","logger","method","HTTP_VERBS","PUT","rawError","send","_this5","_request$muteOptions","muteOptions","_promise","Defer","newRequest","bind","defer","promise","isConfluenceCreated","downgradeFromMultistreamToTranscoded","preferTranscoding","WebexPlugin"],"sources":["locusMediaRequest.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {defer} from 'lodash';\nimport {Defer} from '@webex/common';\nimport {WebexPlugin} from '@webex/webex-core';\nimport {MEDIA, HTTP_VERBS, ROAP} from '../constants';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {ClientMediaPreferences} from '../reachability/reachability.types';\n\nexport type MediaRequestType = 'RoapMessage' | 'LocalMute';\nexport type RequestResult = any;\n\nexport type RoapRequest = {\n type: 'RoapMessage';\n selfUrl: string;\n mediaId: string;\n roapMessage: any;\n reachability: any;\n clientMediaPreferences: ClientMediaPreferences;\n sequence?: any;\n};\n\nexport type LocalMuteRequest = {\n type: 'LocalMute';\n selfUrl: string;\n mediaId: string;\n sequence?: any;\n muteOptions: {\n audioMuted?: boolean;\n videoMuted?: boolean;\n };\n};\n\nexport type Request = RoapRequest | LocalMuteRequest;\n\n/** Class representing a single /media request being sent to Locus */\nclass InternalRequestInfo {\n public readonly request: Request;\n private pendingPromises: Defer[];\n private sendRequestFn: (request: Request) => Promise<RequestResult>;\n\n /** Constructor */\n constructor(\n request: Request,\n pendingPromise: Defer,\n sendRequestFn: (request: Request) => Promise<RequestResult>\n ) {\n this.request = request;\n this.pendingPromises = [pendingPromise];\n this.sendRequestFn = sendRequestFn;\n }\n\n /**\n * Returns the list of pending promises associated with this request\n */\n public getPendingPromises() {\n return this.pendingPromises;\n }\n\n /**\n * Adds promises to the list of pending promises associated with this request\n */\n public addPendingPromises(pendingPromises: Defer[]) {\n this.pendingPromises.push(...pendingPromises);\n }\n\n /**\n * Executes the request. Returned promise is resolved once the request\n * is completed (no matter if it succeeded or failed).\n */\n public execute(): Promise<void> {\n return this.sendRequestFn(this.request)\n .then((result) => {\n // resolve all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.resolve(result));\n })\n .catch((e) => {\n // reject all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.reject(e));\n });\n }\n}\n\nexport type Config = {\n device: {\n url: string;\n deviceType: string;\n countryCode?: string;\n regionCode?: string;\n };\n correlationId: string;\n meetingId: string;\n preferTranscoding: boolean;\n};\n\n/**\n * Returns true if the request is triggering confluence creation in the server\n */\nfunction isRequestAffectingConfluenceState(request: Request): boolean {\n return (\n request.type === 'RoapMessage' && request.roapMessage.messageType === ROAP.ROAP_TYPES.OFFER\n );\n}\n\n/**\n * This class manages all /media API requests to Locus. Every call to that\n * Locus API has to go through this class.\n */\nexport class LocusMediaRequest extends WebexPlugin {\n private config: Config;\n private latestAudioMuted?: boolean;\n private latestVideoMuted?: boolean;\n private isRequestInProgress: boolean;\n private queuedRequests: InternalRequestInfo[];\n private confluenceState: 'not created' | 'creation in progress' | 'created';\n /**\n * Constructor\n */\n constructor(config: Config, options: any) {\n super({}, options);\n this.isRequestInProgress = false;\n this.queuedRequests = [];\n this.config = config;\n this.confluenceState = 'not created';\n }\n\n /**\n * Add a request to the internal queue.\n */\n private addToQueue(info: InternalRequestInfo) {\n if (info.request.type === 'LocalMute' && this.queuedRequests.length > 0) {\n // We don't need additional local mute requests in the queue.\n // We only need at most 1 local mute or 1 roap request, because\n // roap requests also include mute state, so whatever request\n // is sent out, it will send the latest local mute state.\n // We only need to store the pendingPromises so that they get resolved\n // when the roap request is sent out.\n this.queuedRequests[0].addPendingPromises(info.getPendingPromises());\n\n return;\n }\n\n if (info.request.type === 'RoapMessage' && this.queuedRequests.length > 0) {\n // remove any LocalMute requests from the queue, because this Roap message\n // will also update the mute status in Locus, so they are redundant\n this.queuedRequests = this.queuedRequests.filter((r) => {\n if (r.request.type === 'LocalMute') {\n // we need to keep the pending promises from the local mute request\n // that we're removing from the queue\n info.addPendingPromises(r.getPendingPromises());\n\n return false;\n }\n\n return true;\n });\n }\n\n this.queuedRequests.push(info);\n }\n\n /**\n * Takes the next request from the queue and executes it. Once that\n * request is completed, the next one will be taken from the queue\n * and executed and this is repeated until the queue is empty.\n */\n private executeNextQueuedRequest(): void {\n if (this.isRequestInProgress) {\n return;\n }\n\n const nextRequest = this.queuedRequests.shift();\n\n if (nextRequest) {\n this.isRequestInProgress = true;\n nextRequest.execute().then(() => {\n this.isRequestInProgress = false;\n this.executeNextQueuedRequest();\n });\n }\n }\n\n /**\n * Returns latest requested audio and video mute values. If they have never been\n * requested, we assume audio/video to be muted.\n */\n private getLatestMuteState() {\n const audioMuted = this.latestAudioMuted !== undefined ? this.latestAudioMuted : true;\n const videoMuted = this.latestVideoMuted !== undefined ? this.latestVideoMuted : true;\n\n return {audioMuted, videoMuted};\n }\n\n /**\n * Prepares the uri and body for the media request to be sent to Locus\n */\n private sendHttpRequest(request: Request) {\n const uri = `${request.selfUrl}/${MEDIA}`;\n\n const {audioMuted, videoMuted} = this.getLatestMuteState();\n\n // first setup things common to all requests\n const body: any = {\n device: this.config.device,\n correlationId: this.config.correlationId,\n };\n\n const localMedias: any = {\n audioMuted,\n videoMuted,\n };\n\n // now add things specific to request type\n switch (request.type) {\n case 'LocalMute':\n body.respOnlySdp = true;\n body.usingResource = null;\n break;\n\n case 'RoapMessage':\n localMedias.roapMessage = request.roapMessage;\n localMedias.reachability = request.reachability;\n body.clientMediaPreferences = request.clientMediaPreferences;\n\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.request',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n break;\n }\n\n if (request.sequence) {\n body.sequence = request.sequence;\n }\n\n body.localMedias = [\n {\n localSdp: JSON.stringify(localMedias), // this part must be JSON stringified, Locus requires this\n mediaId: request.mediaId,\n },\n ];\n\n LoggerProxy.logger.info(\n `Meeting:LocusMediaRequest#sendHttpRequest --> ${request.type} audioMuted=${audioMuted} videoMuted=${videoMuted}`\n );\n\n if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {\n this.confluenceState = 'creation in progress';\n }\n\n // @ts-ignore\n return this.request({\n method: HTTP_VERBS.PUT,\n uri,\n body,\n })\n .then((result) => {\n if (isRequestAffectingConfluenceState(request)) {\n this.confluenceState = 'created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n }\n\n return result;\n })\n .catch((e) => {\n if (\n isRequestAffectingConfluenceState(request) &&\n this.confluenceState === 'creation in progress'\n ) {\n this.confluenceState = 'not created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n rawError: e,\n },\n });\n }\n\n throw e;\n });\n }\n\n /**\n * Sends a media request to Locus\n */\n public send(request: Request): Promise<RequestResult> {\n if (request.type === 'LocalMute') {\n const {audioMuted, videoMuted} = request.muteOptions;\n\n if (audioMuted !== undefined) {\n this.latestAudioMuted = audioMuted;\n }\n if (videoMuted !== undefined) {\n this.latestVideoMuted = videoMuted;\n }\n\n if (this.confluenceState === 'not created') {\n // if there is no confluence, there is no point sending out local mute request\n // as it will fail so we just store the latest audio/video muted values\n // and resolve immediately, so that higher layer (MuteState class) doesn't get blocked\n // and can call us again if user mutes/unmutes again before confluence is created\n LoggerProxy.logger.info(\n 'Meeting:LocusMediaRequest#send --> called with LocalMute request before confluence creation'\n );\n\n return Promise.resolve({});\n }\n }\n\n const pendingPromise = new Defer();\n\n const newRequest = new InternalRequestInfo(\n request,\n pendingPromise,\n this.sendHttpRequest.bind(this)\n );\n\n this.addToQueue(newRequest);\n\n defer(() => this.executeNextQueuedRequest());\n\n return pendingPromise.promise;\n }\n\n /** Returns true if a confluence on the server is already created */\n public isConfluenceCreated() {\n return this.confluenceState === 'created';\n }\n\n /**\n * This method needs to be called when we downgrade from multistream to transcoded connection.\n */\n public downgradeFromMultistreamToTranscoded() {\n this.config.preferTranscoding = true;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["_lodash","require","_common","_events","_webexCore","_constants","_loggerProxy","_interopRequireDefault","_createSuper","Derived","hasNativeReflectConstruct","_isNativeReflectConstruct","_createSuperInternal","Super","_getPrototypeOf2","default","result","NewTarget","constructor","_Reflect$construct","arguments","apply","_possibleConstructorReturn2","Reflect","sham","Proxy","Boolean","prototype","valueOf","call","e","InternalRequestInfo","request","pendingPromise","sendRequestFn","_classCallCheck2","_defineProperty2","pendingPromises","_createClass2","key","value","getPendingPromises","addPendingPromises","_this$pendingPromises","push","_toConsumableArray2","execute","_this","then","forEach","d","resolve","catch","reject","isRequestAffectingConfluenceState","type","roapMessage","messageType","ROAP","ROAP_TYPES","OFFER","LocusMediaRequest","exports","_WebexPlugin","_inherits2","_super","config","options","_this2","_assertThisInitialized2","isRequestInProgress","queuedRequests","confluenceState","addToQueue","info","length","filter","r","executeNextQueuedRequest","_this3","nextRequest","shift","getLatestMuteState","audioMuted","latestAudioMuted","undefined","videoMuted","latestVideoMuted","sendHttpRequest","_this4","uri","concat","selfUrl","MEDIA","_this$getLatestMuteSt","body","device","correlationId","localMedias","respOnlySdp","usingResource","reachability","clientMediaPreferences","webex","internal","newMetrics","submitClientEvent","name","meetingId","sequence","localSdp","_stringify","mediaId","LoggerProxy","logger","upload","EventEmitter","download","method","HTTP_VERBS","PUT","promise","rawError","setupProgressListener","direction","eventEmitter","on","progressEvent","timeStamp","loaded","total","send","_this5","_request$muteOptions","muteOptions","_promise","Defer","newRequest","bind","defer","isConfluenceCreated","downgradeFromMultistreamToTranscoded","preferTranscoding","WebexPlugin"],"sources":["locusMediaRequest.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {defer} from 'lodash';\nimport {Defer, transferEvents} from '@webex/common';\nimport {EventEmitter} from 'events';\nimport {WebexPlugin} from '@webex/webex-core';\nimport {MEDIA, HTTP_VERBS, ROAP} from '../constants';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {ClientMediaPreferences} from '../reachability/reachability.types';\n\nexport type MediaRequestType = 'RoapMessage' | 'LocalMute';\nexport type RequestResult = any;\n\nexport type RoapRequest = {\n type: 'RoapMessage';\n selfUrl: string;\n mediaId: string;\n roapMessage: any;\n reachability: any;\n clientMediaPreferences: ClientMediaPreferences;\n sequence?: any;\n};\n\nexport type LocalMuteRequest = {\n type: 'LocalMute';\n selfUrl: string;\n mediaId: string;\n sequence?: any;\n muteOptions: {\n audioMuted?: boolean;\n videoMuted?: boolean;\n };\n};\n\nexport type Request = RoapRequest | LocalMuteRequest;\n\n/** Class representing a single /media request being sent to Locus */\nclass InternalRequestInfo {\n public readonly request: Request;\n private pendingPromises: Defer[];\n private sendRequestFn: (request: Request) => Promise<RequestResult>;\n\n /** Constructor */\n constructor(\n request: Request,\n pendingPromise: Defer,\n sendRequestFn: (request: Request) => Promise<RequestResult>\n ) {\n this.request = request;\n this.pendingPromises = [pendingPromise];\n this.sendRequestFn = sendRequestFn;\n }\n\n /**\n * Returns the list of pending promises associated with this request\n */\n public getPendingPromises() {\n return this.pendingPromises;\n }\n\n /**\n * Adds promises to the list of pending promises associated with this request\n */\n public addPendingPromises(pendingPromises: Defer[]) {\n this.pendingPromises.push(...pendingPromises);\n }\n\n /**\n * Executes the request. Returned promise is resolved once the request\n * is completed (no matter if it succeeded or failed).\n */\n public execute(): Promise<void> {\n return this.sendRequestFn(this.request)\n .then((result) => {\n // resolve all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.resolve(result));\n })\n .catch((e) => {\n // reject all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.reject(e));\n });\n }\n}\n\nexport type Config = {\n device: {\n url: string;\n deviceType: string;\n countryCode?: string;\n regionCode?: string;\n };\n correlationId: string;\n meetingId: string;\n preferTranscoding: boolean;\n};\n\n/**\n * Returns true if the request is triggering confluence creation in the server\n */\nfunction isRequestAffectingConfluenceState(request: Request): boolean {\n return (\n request.type === 'RoapMessage' && request.roapMessage.messageType === ROAP.ROAP_TYPES.OFFER\n );\n}\n\n/**\n * This class manages all /media API requests to Locus. Every call to that\n * Locus API has to go through this class.\n */\nexport class LocusMediaRequest extends WebexPlugin {\n private config: Config;\n private latestAudioMuted?: boolean;\n private latestVideoMuted?: boolean;\n private isRequestInProgress: boolean;\n private queuedRequests: InternalRequestInfo[];\n private confluenceState: 'not created' | 'creation in progress' | 'created';\n /**\n * Constructor\n */\n constructor(config: Config, options: any) {\n super({}, options);\n this.isRequestInProgress = false;\n this.queuedRequests = [];\n this.config = config;\n this.confluenceState = 'not created';\n }\n\n /**\n * Add a request to the internal queue.\n */\n private addToQueue(info: InternalRequestInfo) {\n if (info.request.type === 'LocalMute' && this.queuedRequests.length > 0) {\n // We don't need additional local mute requests in the queue.\n // We only need at most 1 local mute or 1 roap request, because\n // roap requests also include mute state, so whatever request\n // is sent out, it will send the latest local mute state.\n // We only need to store the pendingPromises so that they get resolved\n // when the roap request is sent out.\n this.queuedRequests[0].addPendingPromises(info.getPendingPromises());\n\n return;\n }\n\n if (info.request.type === 'RoapMessage' && this.queuedRequests.length > 0) {\n // remove any LocalMute requests from the queue, because this Roap message\n // will also update the mute status in Locus, so they are redundant\n this.queuedRequests = this.queuedRequests.filter((r) => {\n if (r.request.type === 'LocalMute') {\n // we need to keep the pending promises from the local mute request\n // that we're removing from the queue\n info.addPendingPromises(r.getPendingPromises());\n\n return false;\n }\n\n return true;\n });\n }\n\n this.queuedRequests.push(info);\n }\n\n /**\n * Takes the next request from the queue and executes it. Once that\n * request is completed, the next one will be taken from the queue\n * and executed and this is repeated until the queue is empty.\n */\n private executeNextQueuedRequest(): void {\n if (this.isRequestInProgress) {\n return;\n }\n\n const nextRequest = this.queuedRequests.shift();\n\n if (nextRequest) {\n this.isRequestInProgress = true;\n nextRequest.execute().then(() => {\n this.isRequestInProgress = false;\n this.executeNextQueuedRequest();\n });\n }\n }\n\n /**\n * Returns latest requested audio and video mute values. If they have never been\n * requested, we assume audio/video to be muted.\n */\n private getLatestMuteState() {\n const audioMuted = this.latestAudioMuted !== undefined ? this.latestAudioMuted : true;\n const videoMuted = this.latestVideoMuted !== undefined ? this.latestVideoMuted : true;\n\n return {audioMuted, videoMuted};\n }\n\n /**\n * Prepares the uri and body for the media request to be sent to Locus\n */\n private sendHttpRequest(request: Request) {\n const uri = `${request.selfUrl}/${MEDIA}`;\n\n const {audioMuted, videoMuted} = this.getLatestMuteState();\n\n // first setup things common to all requests\n const body: any = {\n device: this.config.device,\n correlationId: this.config.correlationId,\n };\n\n const localMedias: any = {\n audioMuted,\n videoMuted,\n };\n\n // now add things specific to request type\n switch (request.type) {\n case 'LocalMute':\n body.respOnlySdp = true;\n body.usingResource = null;\n break;\n\n case 'RoapMessage':\n localMedias.roapMessage = request.roapMessage;\n localMedias.reachability = request.reachability;\n body.clientMediaPreferences = request.clientMediaPreferences;\n\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.request',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n break;\n }\n\n if (request.sequence) {\n body.sequence = request.sequence;\n }\n\n body.localMedias = [\n {\n localSdp: JSON.stringify(localMedias), // this part must be JSON stringified, Locus requires this\n mediaId: request.mediaId,\n },\n ];\n\n LoggerProxy.logger.info(\n `Meeting:LocusMediaRequest#sendHttpRequest --> ${request.type} audioMuted=${audioMuted} videoMuted=${videoMuted}`\n );\n\n if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {\n this.confluenceState = 'creation in progress';\n }\n\n const upload = new EventEmitter();\n const download = new EventEmitter();\n\n const options = {\n method: HTTP_VERBS.PUT,\n uri,\n body,\n upload,\n download,\n };\n\n // @ts-ignore\n const promise = this.request(options)\n .then((result) => {\n if (isRequestAffectingConfluenceState(request)) {\n this.confluenceState = 'created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n }\n\n return result;\n })\n .catch((e) => {\n if (\n isRequestAffectingConfluenceState(request) &&\n this.confluenceState === 'creation in progress'\n ) {\n this.confluenceState = 'not created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n rawError: e,\n },\n });\n }\n\n throw e;\n });\n\n if (request.type === 'RoapMessage') {\n const setupProgressListener = (direction: string, eventEmitter: EventEmitter) => {\n eventEmitter.on('progress', (progressEvent: ProgressEvent) => {\n LoggerProxy.logger.info(\n `${request.type}: ${direction} Progress, Timestamp: ${progressEvent.timeStamp}, Progress: ${progressEvent.loaded}/${progressEvent.total}`\n );\n });\n };\n\n setupProgressListener('Upload', options.upload);\n setupProgressListener('Download', options.download);\n }\n\n return promise;\n }\n\n /**\n * Sends a media request to Locus\n */\n public send(request: Request): Promise<RequestResult> {\n if (request.type === 'LocalMute') {\n const {audioMuted, videoMuted} = request.muteOptions;\n\n if (audioMuted !== undefined) {\n this.latestAudioMuted = audioMuted;\n }\n if (videoMuted !== undefined) {\n this.latestVideoMuted = videoMuted;\n }\n\n if (this.confluenceState === 'not created') {\n // if there is no confluence, there is no point sending out local mute request\n // as it will fail so we just store the latest audio/video muted values\n // and resolve immediately, so that higher layer (MuteState class) doesn't get blocked\n // and can call us again if user mutes/unmutes again before confluence is created\n LoggerProxy.logger.info(\n 'Meeting:LocusMediaRequest#send --> called with LocalMute request before confluence creation'\n );\n\n return Promise.resolve({});\n }\n }\n\n const pendingPromise = new Defer();\n\n const newRequest = new InternalRequestInfo(\n request,\n pendingPromise,\n this.sendHttpRequest.bind(this)\n );\n\n this.addToQueue(newRequest);\n\n defer(() => this.executeNextQueuedRequest());\n\n return pendingPromise.promise;\n }\n\n /** Returns true if a confluence on the server is already created */\n public isConfluenceCreated() {\n return this.confluenceState === 'created';\n }\n\n /**\n * This method needs to be called when we downgrade from multistream to transcoded connection.\n */\n public downgradeFromMultistreamToTranscoded() {\n this.config.preferTranscoding = true;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,UAAA,GAAAH,OAAA;AACA,IAAAI,UAAA,GAAAJ,OAAA;AACA,IAAAK,YAAA,GAAAC,sBAAA,CAAAN,OAAA;AAAsD,SAAAO,aAAAC,OAAA,QAAAC,yBAAA,GAAAC,yBAAA,oBAAAC,qBAAA,QAAAC,KAAA,OAAAC,gBAAA,CAAAC,OAAA,EAAAN,OAAA,GAAAO,MAAA,MAAAN,yBAAA,QAAAO,SAAA,OAAAH,gBAAA,CAAAC,OAAA,QAAAG,WAAA,EAAAF,MAAA,GAAAG,kBAAA,CAAAN,KAAA,EAAAO,SAAA,EAAAH,SAAA,YAAAD,MAAA,GAAAH,KAAA,CAAAQ,KAAA,OAAAD,SAAA,gBAAAE,2BAAA,CAAAP,OAAA,QAAAC,MAAA;AAAA,SAAAL,0BAAA,eAAAY,OAAA,qBAAAJ,kBAAA,oBAAAA,kBAAA,CAAAK,IAAA,2BAAAC,KAAA,oCAAAC,OAAA,CAAAC,SAAA,CAAAC,OAAA,CAAAC,IAAA,CAAAV,kBAAA,CAAAO,OAAA,8CAAAI,CAAA,sBANtD;AAmCA;AAAA,IACMC,mBAAmB;EAKvB;EACA,SAAAA,oBACEC,OAAgB,EAChBC,cAAqB,EACrBC,aAA2D,EAC3D;IAAA,IAAAC,gBAAA,CAAApB,OAAA,QAAAgB,mBAAA;IAAA,IAAAK,gBAAA,CAAArB,OAAA;IAAA,IAAAqB,gBAAA,CAAArB,OAAA;IAAA,IAAAqB,gBAAA,CAAArB,OAAA;IACA,IAAI,CAACiB,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACK,eAAe,GAAG,CAACJ,cAAc,CAAC;IACvC,IAAI,CAACC,aAAa,GAAGA,aAAa;EACpC;;EAEA;AACF;AACA;EAFE,IAAAI,aAAA,CAAAvB,OAAA,EAAAgB,mBAAA;IAAAQ,GAAA;IAAAC,KAAA,EAGA,SAAAC,mBAAA,EAA4B;MAC1B,OAAO,IAAI,CAACJ,eAAe;IAC7B;;IAEA;AACF;AACA;EAFE;IAAAE,GAAA;IAAAC,KAAA,EAGA,SAAAE,mBAA0BL,eAAwB,EAAE;MAAA,IAAAM,qBAAA;MAClD,CAAAA,qBAAA,OAAI,CAACN,eAAe,EAACO,IAAI,CAAAvB,KAAA,CAAAsB,qBAAA,MAAAE,mBAAA,CAAA9B,OAAA,EAAIsB,eAAe,EAAC;IAC/C;;IAEA;AACF;AACA;AACA;EAHE;IAAAE,GAAA;IAAAC,KAAA,EAIA,SAAAM,QAAA,EAAgC;MAAA,IAAAC,KAAA;MAC9B,OAAO,IAAI,CAACb,aAAa,CAAC,IAAI,CAACF,OAAO,CAAC,CACpCgB,IAAI,CAAC,UAAChC,MAAM,EAAK;QAChB;QACA+B,KAAI,CAACV,eAAe,CAACY,OAAO,CAAC,UAACC,CAAC;UAAA,OAAKA,CAAC,CAACC,OAAO,CAACnC,MAAM,CAAC;QAAA,EAAC;MACxD,CAAC,CAAC,CACDoC,KAAK,CAAC,UAACtB,CAAC,EAAK;QACZ;QACAiB,KAAI,CAACV,eAAe,CAACY,OAAO,CAAC,UAACC,CAAC;UAAA,OAAKA,CAAC,CAACG,MAAM,CAACvB,CAAC,CAAC;QAAA,EAAC;MAClD,CAAC,CAAC;IACN;EAAC;EAAA,OAAAC,mBAAA;AAAA;AAeH;AACA;AACA;AACA,SAASuB,iCAAiCA,CAACtB,OAAgB,EAAW;EACpE,OACEA,OAAO,CAACuB,IAAI,KAAK,aAAa,IAAIvB,OAAO,CAACwB,WAAW,CAACC,WAAW,KAAKC,eAAI,CAACC,UAAU,CAACC,KAAK;AAE/F;;AAEA;AACA;AACA;AACA;AAHA,IAIaC,iBAAiB,GAAAC,OAAA,CAAAD,iBAAA,0BAAAE,YAAA;EAAA,IAAAC,UAAA,CAAAjD,OAAA,EAAA8C,iBAAA,EAAAE,YAAA;EAAA,IAAAE,MAAA,GAAAzD,YAAA,CAAAqD,iBAAA;EAO5B;AACF;AACA;EACE,SAAAA,kBAAYK,MAAc,EAAEC,OAAY,EAAE;IAAA,IAAAC,MAAA;IAAA,IAAAjC,gBAAA,CAAApB,OAAA,QAAA8C,iBAAA;IACxCO,MAAA,GAAAH,MAAA,CAAApC,IAAA,OAAM,CAAC,CAAC,EAAEsC,OAAO;IAAE,IAAA/B,gBAAA,CAAArB,OAAA,MAAAsD,uBAAA,CAAAtD,OAAA,EAAAqD,MAAA;IAAA,IAAAhC,gBAAA,CAAArB,OAAA,MAAAsD,uBAAA,CAAAtD,OAAA,EAAAqD,MAAA;IAAA,IAAAhC,gBAAA,CAAArB,OAAA,MAAAsD,uBAAA,CAAAtD,OAAA,EAAAqD,MAAA;IAAA,IAAAhC,gBAAA,CAAArB,OAAA,MAAAsD,uBAAA,CAAAtD,OAAA,EAAAqD,MAAA;IAAA,IAAAhC,gBAAA,CAAArB,OAAA,MAAAsD,uBAAA,CAAAtD,OAAA,EAAAqD,MAAA;IAAA,IAAAhC,gBAAA,CAAArB,OAAA,MAAAsD,uBAAA,CAAAtD,OAAA,EAAAqD,MAAA;IACnBA,MAAA,CAAKE,mBAAmB,GAAG,KAAK;IAChCF,MAAA,CAAKG,cAAc,GAAG,EAAE;IACxBH,MAAA,CAAKF,MAAM,GAAGA,MAAM;IACpBE,MAAA,CAAKI,eAAe,GAAG,aAAa;IAAC,OAAAJ,MAAA;EACvC;;EAEA;AACF;AACA;EAFE,IAAA9B,aAAA,CAAAvB,OAAA,EAAA8C,iBAAA;IAAAtB,GAAA;IAAAC,KAAA,EAGA,SAAAiC,WAAmBC,IAAyB,EAAE;MAC5C,IAAIA,IAAI,CAAC1C,OAAO,CAACuB,IAAI,KAAK,WAAW,IAAI,IAAI,CAACgB,cAAc,CAACI,MAAM,GAAG,CAAC,EAAE;QACvE;QACA;QACA;QACA;QACA;QACA;QACA,IAAI,CAACJ,cAAc,CAAC,CAAC,CAAC,CAAC7B,kBAAkB,CAACgC,IAAI,CAACjC,kBAAkB,CAAC,CAAC,CAAC;QAEpE;MACF;MAEA,IAAIiC,IAAI,CAAC1C,OAAO,CAACuB,IAAI,KAAK,aAAa,IAAI,IAAI,CAACgB,cAAc,CAACI,MAAM,GAAG,CAAC,EAAE;QACzE;QACA;QACA,IAAI,CAACJ,cAAc,GAAG,IAAI,CAACA,cAAc,CAACK,MAAM,CAAC,UAACC,CAAC,EAAK;UACtD,IAAIA,CAAC,CAAC7C,OAAO,CAACuB,IAAI,KAAK,WAAW,EAAE;YAClC;YACA;YACAmB,IAAI,CAAChC,kBAAkB,CAACmC,CAAC,CAACpC,kBAAkB,CAAC,CAAC,CAAC;YAE/C,OAAO,KAAK;UACd;UAEA,OAAO,IAAI;QACb,CAAC,CAAC;MACJ;MAEA,IAAI,CAAC8B,cAAc,CAAC3B,IAAI,CAAC8B,IAAI,CAAC;IAChC;;IAEA;AACF;AACA;AACA;AACA;EAJE;IAAAnC,GAAA;IAAAC,KAAA,EAKA,SAAAsC,yBAAA,EAAyC;MAAA,IAAAC,MAAA;MACvC,IAAI,IAAI,CAACT,mBAAmB,EAAE;QAC5B;MACF;MAEA,IAAMU,WAAW,GAAG,IAAI,CAACT,cAAc,CAACU,KAAK,CAAC,CAAC;MAE/C,IAAID,WAAW,EAAE;QACf,IAAI,CAACV,mBAAmB,GAAG,IAAI;QAC/BU,WAAW,CAAClC,OAAO,CAAC,CAAC,CAACE,IAAI,CAAC,YAAM;UAC/B+B,MAAI,CAACT,mBAAmB,GAAG,KAAK;UAChCS,MAAI,CAACD,wBAAwB,CAAC,CAAC;QACjC,CAAC,CAAC;MACJ;IACF;;IAEA;AACF;AACA;AACA;EAHE;IAAAvC,GAAA;IAAAC,KAAA,EAIA,SAAA0C,mBAAA,EAA6B;MAC3B,IAAMC,UAAU,GAAG,IAAI,CAACC,gBAAgB,KAAKC,SAAS,GAAG,IAAI,CAACD,gBAAgB,GAAG,IAAI;MACrF,IAAME,UAAU,GAAG,IAAI,CAACC,gBAAgB,KAAKF,SAAS,GAAG,IAAI,CAACE,gBAAgB,GAAG,IAAI;MAErF,OAAO;QAACJ,UAAU,EAAVA,UAAU;QAAEG,UAAU,EAAVA;MAAU,CAAC;IACjC;;IAEA;AACF;AACA;EAFE;IAAA/C,GAAA;IAAAC,KAAA,EAGA,SAAAgD,gBAAwBxD,OAAgB,EAAE;MAAA,IAAAyD,MAAA;MACxC,IAAMC,GAAG,MAAAC,MAAA,CAAM3D,OAAO,CAAC4D,OAAO,OAAAD,MAAA,CAAIE,gBAAK,CAAE;MAEzC,IAAAC,qBAAA,GAAiC,IAAI,CAACZ,kBAAkB,CAAC,CAAC;QAAnDC,UAAU,GAAAW,qBAAA,CAAVX,UAAU;QAAEG,UAAU,GAAAQ,qBAAA,CAAVR,UAAU;;MAE7B;MACA,IAAMS,IAAS,GAAG;QAChBC,MAAM,EAAE,IAAI,CAAC9B,MAAM,CAAC8B,MAAM;QAC1BC,aAAa,EAAE,IAAI,CAAC/B,MAAM,CAAC+B;MAC7B,CAAC;MAED,IAAMC,WAAgB,GAAG;QACvBf,UAAU,EAAVA,UAAU;QACVG,UAAU,EAAVA;MACF,CAAC;;MAED;MACA,QAAQtD,OAAO,CAACuB,IAAI;QAClB,KAAK,WAAW;UACdwC,IAAI,CAACI,WAAW,GAAG,IAAI;UACvBJ,IAAI,CAACK,aAAa,GAAG,IAAI;UACzB;QAEF,KAAK,aAAa;UAChBF,WAAW,CAAC1C,WAAW,GAAGxB,OAAO,CAACwB,WAAW;UAC7C0C,WAAW,CAACG,YAAY,GAAGrE,OAAO,CAACqE,YAAY;UAC/CN,IAAI,CAACO,sBAAsB,GAAGtE,OAAO,CAACsE,sBAAsB;;UAE5D;UACA,IAAI,CAACC,KAAK,CAACC,QAAQ,CAACC,UAAU,CAACC,iBAAiB,CAAC;YAC/CC,IAAI,EAAE,4BAA4B;YAClCxC,OAAO,EAAE;cACPyC,SAAS,EAAE,IAAI,CAAC1C,MAAM,CAAC0C;YACzB;UACF,CAAC,CAAC;UACF;MACJ;MAEA,IAAI5E,OAAO,CAAC6E,QAAQ,EAAE;QACpBd,IAAI,CAACc,QAAQ,GAAG7E,OAAO,CAAC6E,QAAQ;MAClC;MAEAd,IAAI,CAACG,WAAW,GAAG,CACjB;QACEY,QAAQ,EAAE,IAAAC,UAAA,CAAAhG,OAAA,EAAemF,WAAW,CAAC;QAAE;QACvCc,OAAO,EAAEhF,OAAO,CAACgF;MACnB,CAAC,CACF;MAEDC,oBAAW,CAACC,MAAM,CAACxC,IAAI,kDAAAiB,MAAA,CAC4B3D,OAAO,CAACuB,IAAI,kBAAAoC,MAAA,CAAeR,UAAU,kBAAAQ,MAAA,CAAeL,UAAU,CACjH,CAAC;MAED,IAAIhC,iCAAiC,CAACtB,OAAO,CAAC,IAAI,IAAI,CAACwC,eAAe,KAAK,aAAa,EAAE;QACxF,IAAI,CAACA,eAAe,GAAG,sBAAsB;MAC/C;MAEA,IAAM2C,MAAM,GAAG,IAAIC,oBAAY,CAAC,CAAC;MACjC,IAAMC,QAAQ,GAAG,IAAID,oBAAY,CAAC,CAAC;MAEnC,IAAMjD,OAAO,GAAG;QACdmD,MAAM,EAAEC,qBAAU,CAACC,GAAG;QACtB9B,GAAG,EAAHA,GAAG;QACHK,IAAI,EAAJA,IAAI;QACJoB,MAAM,EAANA,MAAM;QACNE,QAAQ,EAARA;MACF,CAAC;;MAED;MACA,IAAMI,OAAO,GAAG,IAAI,CAACzF,OAAO,CAACmC,OAAO,CAAC,CAClCnB,IAAI,CAAC,UAAChC,MAAM,EAAK;QAChB,IAAIsC,iCAAiC,CAACtB,OAAO,CAAC,EAAE;UAC9CyD,MAAI,CAACjB,eAAe,GAAG,SAAS;QAClC;QAEA,IAAIxC,OAAO,CAACuB,IAAI,KAAK,aAAa,EAAE;UAClC;UACAkC,MAAI,CAACc,KAAK,CAACC,QAAQ,CAACC,UAAU,CAACC,iBAAiB,CAAC;YAC/CC,IAAI,EAAE,6BAA6B;YACnCxC,OAAO,EAAE;cACPyC,SAAS,EAAEnB,MAAI,CAACvB,MAAM,CAAC0C;YACzB;UACF,CAAC,CAAC;QACJ;QAEA,OAAO5F,MAAM;MACf,CAAC,CAAC,CACDoC,KAAK,CAAC,UAACtB,CAAC,EAAK;QACZ,IACEwB,iCAAiC,CAACtB,OAAO,CAAC,IAC1CyD,MAAI,CAACjB,eAAe,KAAK,sBAAsB,EAC/C;UACAiB,MAAI,CAACjB,eAAe,GAAG,aAAa;QACtC;QAEA,IAAIxC,OAAO,CAACuB,IAAI,KAAK,aAAa,EAAE;UAClC;UACAkC,MAAI,CAACc,KAAK,CAACC,QAAQ,CAACC,UAAU,CAACC,iBAAiB,CAAC;YAC/CC,IAAI,EAAE,6BAA6B;YACnCxC,OAAO,EAAE;cACPyC,SAAS,EAAEnB,MAAI,CAACvB,MAAM,CAAC0C,SAAS;cAChCc,QAAQ,EAAE5F;YACZ;UACF,CAAC,CAAC;QACJ;QAEA,MAAMA,CAAC;MACT,CAAC,CAAC;MAEJ,IAAIE,OAAO,CAACuB,IAAI,KAAK,aAAa,EAAE;QAClC,IAAMoE,qBAAqB,GAAG,SAAxBA,qBAAqBA,CAAIC,SAAiB,EAAEC,YAA0B,EAAK;UAC/EA,YAAY,CAACC,EAAE,CAAC,UAAU,EAAE,UAACC,aAA4B,EAAK;YAC5Dd,oBAAW,CAACC,MAAM,CAACxC,IAAI,IAAAiB,MAAA,CAClB3D,OAAO,CAACuB,IAAI,QAAAoC,MAAA,CAAKiC,SAAS,4BAAAjC,MAAA,CAAyBoC,aAAa,CAACC,SAAS,kBAAArC,MAAA,CAAeoC,aAAa,CAACE,MAAM,OAAAtC,MAAA,CAAIoC,aAAa,CAACG,KAAK,CACzI,CAAC;UACH,CAAC,CAAC;QACJ,CAAC;QAEDP,qBAAqB,CAAC,QAAQ,EAAExD,OAAO,CAACgD,MAAM,CAAC;QAC/CQ,qBAAqB,CAAC,UAAU,EAAExD,OAAO,CAACkD,QAAQ,CAAC;MACrD;MAEA,OAAOI,OAAO;IAChB;;IAEA;AACF;AACA;EAFE;IAAAlF,GAAA;IAAAC,KAAA,EAGA,SAAA2F,KAAYnG,OAAgB,EAA0B;MAAA,IAAAoG,MAAA;MACpD,IAAIpG,OAAO,CAACuB,IAAI,KAAK,WAAW,EAAE;QAChC,IAAA8E,oBAAA,GAAiCrG,OAAO,CAACsG,WAAW;UAA7CnD,UAAU,GAAAkD,oBAAA,CAAVlD,UAAU;UAAEG,UAAU,GAAA+C,oBAAA,CAAV/C,UAAU;QAE7B,IAAIH,UAAU,KAAKE,SAAS,EAAE;UAC5B,IAAI,CAACD,gBAAgB,GAAGD,UAAU;QACpC;QACA,IAAIG,UAAU,KAAKD,SAAS,EAAE;UAC5B,IAAI,CAACE,gBAAgB,GAAGD,UAAU;QACpC;QAEA,IAAI,IAAI,CAACd,eAAe,KAAK,aAAa,EAAE;UAC1C;UACA;UACA;UACA;UACAyC,oBAAW,CAACC,MAAM,CAACxC,IAAI,CACrB,6FACF,CAAC;UAED,OAAO6D,QAAA,CAAAxH,OAAA,CAAQoC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5B;MACF;MAEA,IAAMlB,cAAc,GAAG,IAAIuG,aAAK,CAAC,CAAC;MAElC,IAAMC,UAAU,GAAG,IAAI1G,mBAAmB,CACxCC,OAAO,EACPC,cAAc,EACd,IAAI,CAACuD,eAAe,CAACkD,IAAI,CAAC,IAAI,CAChC,CAAC;MAED,IAAI,CAACjE,UAAU,CAACgE,UAAU,CAAC;MAE3B,IAAAE,aAAK,EAAC;QAAA,OAAMP,MAAI,CAACtD,wBAAwB,CAAC,CAAC;MAAA,EAAC;MAE5C,OAAO7C,cAAc,CAACwF,OAAO;IAC/B;;IAEA;EAAA;IAAAlF,GAAA;IAAAC,KAAA,EACA,SAAAoG,oBAAA,EAA6B;MAC3B,OAAO,IAAI,CAACpE,eAAe,KAAK,SAAS;IAC3C;;IAEA;AACF;AACA;EAFE;IAAAjC,GAAA;IAAAC,KAAA,EAGA,SAAAqG,qCAAA,EAA8C;MAC5C,IAAI,CAAC3E,MAAM,CAAC4E,iBAAiB,GAAG,IAAI;IACtC;EAAC;EAAA,OAAAjF,iBAAA;AAAA,EAzQoCkF,sBAAW"}
@@ -458,7 +458,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
458
458
  }, _callee7);
459
459
  }))();
460
460
  },
461
- version: "3.8.0-next.5"
461
+ version: "3.8.0-next.6"
462
462
  });
463
463
  var _default = exports.default = Webinar;
464
464
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-meetings": "3.8.0-next.5",
46
+ "@webex/plugin-meetings": "3.8.0-next.6",
47
47
  "@webex/plugin-rooms": "3.8.0-next.1",
48
48
  "@webex/test-helper-chai": "3.8.0-next.1",
49
49
  "@webex/test-helper-mocha": "3.8.0-next.1",
@@ -71,7 +71,7 @@
71
71
  "@webex/internal-plugin-metrics": "3.8.0-next.1",
72
72
  "@webex/internal-plugin-support": "3.8.0-next.1",
73
73
  "@webex/internal-plugin-user": "3.8.0-next.1",
74
- "@webex/internal-plugin-voicea": "3.8.0-next.5",
74
+ "@webex/internal-plugin-voicea": "3.8.0-next.6",
75
75
  "@webex/media-helpers": "3.8.0-next.1",
76
76
  "@webex/plugin-people": "3.8.0-next.1",
77
77
  "@webex/plugin-rooms": "3.8.0-next.1",
@@ -92,5 +92,5 @@
92
92
  "//": [
93
93
  "TODO: upgrade jwt-decode when moving to node 18"
94
94
  ],
95
- "version": "3.8.0-next.5"
95
+ "version": "3.8.0-next.6"
96
96
  }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable valid-jsdoc */
2
2
  import {defer} from 'lodash';
3
- import {Defer} from '@webex/common';
3
+ import {Defer, transferEvents} from '@webex/common';
4
+ import {EventEmitter} from 'events';
4
5
  import {WebexPlugin} from '@webex/webex-core';
5
6
  import {MEDIA, HTTP_VERBS, ROAP} from '../constants';
6
7
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -250,12 +251,19 @@ export class LocusMediaRequest extends WebexPlugin {
250
251
  this.confluenceState = 'creation in progress';
251
252
  }
252
253
 
253
- // @ts-ignore
254
- return this.request({
254
+ const upload = new EventEmitter();
255
+ const download = new EventEmitter();
256
+
257
+ const options = {
255
258
  method: HTTP_VERBS.PUT,
256
259
  uri,
257
260
  body,
258
- })
261
+ upload,
262
+ download,
263
+ };
264
+
265
+ // @ts-ignore
266
+ const promise = this.request(options)
259
267
  .then((result) => {
260
268
  if (isRequestAffectingConfluenceState(request)) {
261
269
  this.confluenceState = 'created';
@@ -294,6 +302,21 @@ export class LocusMediaRequest extends WebexPlugin {
294
302
 
295
303
  throw e;
296
304
  });
305
+
306
+ if (request.type === 'RoapMessage') {
307
+ const setupProgressListener = (direction: string, eventEmitter: EventEmitter) => {
308
+ eventEmitter.on('progress', (progressEvent: ProgressEvent) => {
309
+ LoggerProxy.logger.info(
310
+ `${request.type}: ${direction} Progress, Timestamp: ${progressEvent.timeStamp}, Progress: ${progressEvent.loaded}/${progressEvent.total}`
311
+ );
312
+ });
313
+ };
314
+
315
+ setupProgressListener('Upload', options.upload);
316
+ setupProgressListener('Download', options.download);
317
+ }
318
+
319
+ return promise;
297
320
  }
298
321
 
299
322
  /**
@@ -93,13 +93,14 @@ import CaptchaError from '../../../../src/common/errors/captcha-error';
93
93
  import PermissionError from '../../../../src/common/errors/permission';
94
94
  import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
95
95
  import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
96
- import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';;
96
+ import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';
97
97
  import testUtils from '../../../utils/testUtils';
98
98
  import {
99
99
  MeetingInfoV2CaptchaError,
100
100
  MeetingInfoV2PasswordError,
101
101
  MeetingInfoV2PolicyError,
102
- MeetingInfoV2JoinWebinarError, MeetingInfoV2JoinForbiddenError,
102
+ MeetingInfoV2JoinWebinarError,
103
+ MeetingInfoV2JoinForbiddenError,
103
104
  } from '../../../../src/meeting-info/meeting-info-v2';
104
105
  import {
105
106
  DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
@@ -115,7 +116,8 @@ import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
115
116
 
116
117
  import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
117
118
  import { createBrbState } from '@webex/plugin-meetings/src/meeting/brbState';
118
- import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
119
+ import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
120
+ import { EventEmitter } from 'stream';
119
121
 
120
122
  describe('plugin-meetings', () => {
121
123
  const logger = {
@@ -208,6 +210,7 @@ describe('plugin-meetings', () => {
208
210
  let membersSpy;
209
211
  let meetingRequestSpy;
210
212
  let correlationId;
213
+ let uploadEvent;
211
214
 
212
215
  beforeEach(() => {
213
216
  webex = new MockWebex({
@@ -277,6 +280,8 @@ describe('plugin-meetings', () => {
277
280
  test4 = `test4-${uuid.v4()}`;
278
281
  testDestination = `testDestination-${uuid.v4()}`;
279
282
  correlationId = uuid.v4();
283
+ uploadEvent = new EventEmitter();
284
+ uploadEvent.addListener('progress', () => {})
280
285
 
281
286
  meeting = new Meeting(
282
287
  {
@@ -667,7 +672,7 @@ describe('plugin-meetings', () => {
667
672
  beforeEach(() => {
668
673
  meeting.join = sinon.stub().callsFake((joinOptions) => {
669
674
  meeting.isMultistream = joinOptions.enableMultistream;
670
- return Promise.resolve(fakeJoinResult)
675
+ return Promise.resolve(fakeJoinResult);
671
676
  });
672
677
  addMediaInternalStub = sinon
673
678
  .stub(meeting, 'addMediaInternal')
@@ -1070,7 +1075,11 @@ describe('plugin-meetings', () => {
1070
1075
  mediaOptions,
1071
1076
  });
1072
1077
 
1073
- assert.deepEqual(result, {join: fakeJoinResult, media: undefined, multistreamEnabled: false});
1078
+ assert.deepEqual(result, {
1079
+ join: fakeJoinResult,
1080
+ media: undefined,
1081
+ multistreamEnabled: false,
1082
+ });
1074
1083
 
1075
1084
  assert.calledOnce(meeting.join);
1076
1085
 
@@ -1174,7 +1183,10 @@ describe('plugin-meetings', () => {
1174
1183
  type: addMediaError.name,
1175
1184
  }
1176
1185
  );
1177
- assert.calledOnceWithExactly(meeting.leave, {resourceId: undefined, reason: 'joinWithMedia failure'})
1186
+ assert.calledOnceWithExactly(meeting.leave, {
1187
+ resourceId: undefined,
1188
+ reason: 'joinWithMedia failure',
1189
+ });
1178
1190
  });
1179
1191
  });
1180
1192
 
@@ -3550,18 +3562,18 @@ describe('plugin-meetings', () => {
3550
3562
  it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
3551
3563
  let fakeMembersCollection = {
3552
3564
  members: {
3553
- member1: { isInMeeting: true },
3554
- member2: { isInMeeting: true },
3555
- member3: { isInMeeting: false },
3565
+ member1: {isInMeeting: true},
3566
+ member2: {isInMeeting: true},
3567
+ member3: {isInMeeting: false},
3556
3568
  },
3557
3569
  };
3558
- sinon.stub(meeting, 'getMembers').returns({ membersCollection: fakeMembersCollection });
3559
- const fakeData = { intervalMetadata: {}, networkType: 'wifi' };
3570
+ sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
3571
+ const fakeData = {intervalMetadata: {}, networkType: 'wifi'};
3560
3572
 
3561
3573
  statsAnalyzerStub.emit(
3562
- { file: 'test', function: 'test' },
3574
+ {file: 'test', function: 'test'},
3563
3575
  StatsAnalyzerEventNames.MEDIA_QUALITY,
3564
- { data: fakeData }
3576
+ {data: fakeData}
3565
3577
  );
3566
3578
 
3567
3579
  assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
@@ -3570,15 +3582,17 @@ describe('plugin-meetings', () => {
3570
3582
  meetingId: meeting.id,
3571
3583
  },
3572
3584
  payload: {
3573
- intervals: [sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2))],
3585
+ intervals: [
3586
+ sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
3587
+ ],
3574
3588
  },
3575
3589
  });
3576
3590
  fakeMembersCollection.members.member2.isInMeeting = false;
3577
3591
 
3578
3592
  statsAnalyzerStub.emit(
3579
- { file: 'test', function: 'test' },
3593
+ {file: 'test', function: 'test'},
3580
3594
  StatsAnalyzerEventNames.MEDIA_QUALITY,
3581
- { data: fakeData }
3595
+ {data: fakeData}
3582
3596
  );
3583
3597
 
3584
3598
  assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
@@ -3587,7 +3601,9 @@ describe('plugin-meetings', () => {
3587
3601
  meetingId: meeting.id,
3588
3602
  },
3589
3603
  payload: {
3590
- intervals: [sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 1))],
3604
+ intervals: [
3605
+ sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 1)),
3606
+ ],
3591
3607
  },
3592
3608
  });
3593
3609
  });
@@ -3842,7 +3858,6 @@ describe('plugin-meetings', () => {
3842
3858
  });
3843
3859
 
3844
3860
  describe('when in a multistream meeting', () => {
3845
-
3846
3861
  beforeEach(() => {
3847
3862
  meeting.isMultistream = true;
3848
3863
  });
@@ -3853,7 +3868,7 @@ describe('plugin-meetings', () => {
3853
3868
  await brbResult;
3854
3869
  assert.exists(brbResult.then);
3855
3870
  assert.calledOnce(meeting.brbState.enable);
3856
- })
3871
+ });
3857
3872
 
3858
3873
  it('should disable #beRightBack and return a promise', async () => {
3859
3874
  const brbResult = meeting.beRightBack(false);
@@ -3861,7 +3876,7 @@ describe('plugin-meetings', () => {
3861
3876
  await brbResult;
3862
3877
  assert.exists(brbResult.then);
3863
3878
  assert.calledOnce(meeting.brbState.enable);
3864
- })
3879
+ });
3865
3880
 
3866
3881
  it('should throw an error and reject the promise if setBrb fails', async () => {
3867
3882
  const error = new Error('setBrb failed');
@@ -3874,7 +3889,7 @@ describe('plugin-meetings', () => {
3874
3889
  assert.equal(err.message, 'setBrb failed');
3875
3890
  assert.isRejected((Promise.reject()));
3876
3891
  }
3877
- })
3892
+ });
3878
3893
  });
3879
3894
  });
3880
3895
 
@@ -4006,7 +4021,7 @@ describe('plugin-meetings', () => {
4006
4021
  initiateOffer: sinon.stub().resolves({}),
4007
4022
  update: sinon.stub().resolves({}),
4008
4023
  on: sinon.stub(),
4009
- roapMessageReceived: sinon.stub()
4024
+ roapMessageReceived: sinon.stub(),
4010
4025
  };
4011
4026
 
4012
4027
  fakeMultistreamRoapMediaConnection = {
@@ -4035,7 +4050,7 @@ describe('plugin-meetings', () => {
4035
4050
 
4036
4051
  locusMediaRequestStub = sinon
4037
4052
  .stub(WebexPlugin.prototype, 'request')
4038
- .resolves({body: {locus: {fullState: {}}}});
4053
+ .resolves({body: {locus: {fullState: {}}}, upload: sinon.match.instanceOf(EventEmitter), download: sinon.match.instanceOf(EventEmitter)});
4039
4054
 
4040
4055
  // setup some things and mocks so that the call to join() works
4041
4056
  // (we need to call join() because it creates the LocusMediaRequest instance
@@ -4144,6 +4159,8 @@ describe('plugin-meetings', () => {
4144
4159
  id: 'fake clientMediaPreferences',
4145
4160
  },
4146
4161
  },
4162
+ upload: sinon.match.instanceOf(EventEmitter),
4163
+ download: sinon.match.instanceOf(EventEmitter),
4147
4164
  });
4148
4165
  };
4149
4166
 
@@ -4171,6 +4188,8 @@ describe('plugin-meetings', () => {
4171
4188
  },
4172
4189
  ],
4173
4190
  },
4191
+ upload: sinon.match.instanceOf(EventEmitter),
4192
+ download: sinon.match.instanceOf(EventEmitter),
4174
4193
  });
4175
4194
  };
4176
4195
 
@@ -4195,6 +4214,8 @@ describe('plugin-meetings', () => {
4195
4214
  respOnlySdp: true,
4196
4215
  usingResource: null,
4197
4216
  },
4217
+ upload: sinon.match.instanceOf(EventEmitter),
4218
+ download: sinon.match.instanceOf(EventEmitter),
4198
4219
  });
4199
4220
  };
4200
4221
 
@@ -6337,7 +6358,10 @@ describe('plugin-meetings', () => {
6337
6358
  .throws(new MeetingInfoV2JoinForbiddenError(403003, FAKE_MEETING_INFO)),
6338
6359
  };
6339
6360
 
6340
- await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinForbiddenError);
6361
+ await assert.isRejected(
6362
+ meeting.fetchMeetingInfo({sendCAevents: true}),
6363
+ JoinForbiddenError
6364
+ );
6341
6365
 
6342
6366
  assert.calledWith(
6343
6367
  meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
@@ -6353,10 +6377,7 @@ describe('plugin-meetings', () => {
6353
6377
 
6354
6378
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6355
6379
  assert.equal(meeting.meetingInfoFailureCode, 403003);
6356
- assert.equal(
6357
- meeting.meetingInfoFailureReason,
6358
- MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH
6359
- );
6380
+ assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH);
6360
6381
  assert.equal(meeting.requiredCaptcha, null);
6361
6382
  });
6362
6383
 
@@ -6733,15 +6754,10 @@ describe('plugin-meetings', () => {
6733
6754
  meeting.attrs.meetingInfoProvider = {
6734
6755
  fetchMeetingInfo: sinon
6735
6756
  .stub()
6736
- .throws(
6737
- new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
6738
- ),
6757
+ .throws(new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')),
6739
6758
  };
6740
6759
 
6741
- await assert.isRejected(
6742
- meeting.fetchMeetingInfo({sendCAevents: true}),
6743
- JoinWebinarError
6744
- );
6760
+ await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
6745
6761
 
6746
6762
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6747
6763
  assert.equal(
@@ -6756,15 +6772,10 @@ describe('plugin-meetings', () => {
6756
6772
  meeting.attrs.meetingInfoProvider = {
6757
6773
  fetchMeetingInfo: sinon
6758
6774
  .stub()
6759
- .throws(
6760
- new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
6761
- ),
6775
+ .throws(new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')),
6762
6776
  };
6763
6777
 
6764
- await assert.isRejected(
6765
- meeting.fetchMeetingInfo({sendCAevents: true}),
6766
- JoinWebinarError
6767
- );
6778
+ await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
6768
6779
 
6769
6780
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6770
6781
  assert.equal(
@@ -6779,15 +6790,10 @@ describe('plugin-meetings', () => {
6779
6790
  meeting.attrs.meetingInfoProvider = {
6780
6791
  fetchMeetingInfo: sinon
6781
6792
  .stub()
6782
- .throws(
6783
- new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
6784
- ),
6793
+ .throws(new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')),
6785
6794
  };
6786
6795
 
6787
- await assert.isRejected(
6788
- meeting.fetchMeetingInfo({sendCAevents: true}),
6789
- JoinWebinarError
6790
- );
6796
+ await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
6791
6797
 
6792
6798
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6793
6799
  assert.equal(
@@ -9223,22 +9229,22 @@ describe('plugin-meetings', () => {
9223
9229
  const assertBrb = (enabled) => {
9224
9230
  meeting.brbState = createBrbState(meeting, false);
9225
9231
  meeting.locusInfo.emit(
9226
- { function: 'test', file: 'test' },
9232
+ {function: 'test', file: 'test'},
9227
9233
  LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
9228
- { brb: { enabled } },
9229
- )
9234
+ {brb: {enabled}}
9235
+ );
9230
9236
  assert.calledWithExactly(
9231
9237
  TriggerProxy.trigger,
9232
9238
  meeting,
9233
9239
  {file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
9234
9240
  EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
9235
- { payload: { brb: { enabled } } },
9241
+ {payload: {brb: {enabled}}}
9236
9242
  );
9237
- }
9243
+ };
9238
9244
 
9239
9245
  assertBrb(true);
9240
9246
  assertBrb(false);
9241
- })
9247
+ });
9242
9248
 
9243
9249
  it('listens to the interpretation changed event', () => {
9244
9250
  meeting.simultaneousInterpretation.updateSelfInterpretation = sinon.stub();
@@ -11326,18 +11332,21 @@ describe('plugin-meetings', () => {
11326
11332
  );
11327
11333
  });
11328
11334
 
11329
-
11330
11335
  it('connect ps data channel if ps started in webinar', async () => {
11331
11336
  meeting.joinedWith = {state: 'JOINED'};
11332
- meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url', practiceSessionDatachannelUrl: 'a ps datachannel url'}};
11337
+ meeting.locusInfo = {
11338
+ url: 'a url',
11339
+ info: {
11340
+ datachannelUrl: 'a datachannel url',
11341
+ practiceSessionDatachannelUrl: 'a ps datachannel url',
11342
+ },
11343
+ };
11333
11344
  meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(true);
11334
11345
  await meeting.updateLLMConnection();
11335
11346
 
11336
11347
  assert.notCalled(webex.internal.llm.disconnectLLM);
11337
11348
  assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a ps datachannel url');
11338
-
11339
11349
  });
11340
-
11341
11350
  });
11342
11351
 
11343
11352
  describe('#setLocus', () => {
@@ -11755,24 +11764,29 @@ describe('plugin-meetings', () => {
11755
11764
 
11756
11765
  activeSharingId.whiteboard = beneficiaryId;
11757
11766
 
11758
- eventTrigger.share.push(meeting.webinar.selfIsAttendee ? {
11759
- eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
11760
- functionName: 'remoteShare',
11761
- eventPayload: {
11762
- memberId: null,
11763
- url,
11764
- shareInstanceId,
11765
- annotationInfo: undefined,
11766
- resourceType: undefined,
11767
- },
11768
- } : {
11769
- eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
11770
- functionName: 'startWhiteboardShare',
11771
- eventPayload: {resourceUrl, memberId: beneficiaryId},
11772
- });
11773
-
11774
- shareStatus = meeting.webinar.selfIsAttendee ? SHARE_STATUS.REMOTE_SHARE_ACTIVE : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
11767
+ eventTrigger.share.push(
11768
+ meeting.webinar.selfIsAttendee
11769
+ ? {
11770
+ eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
11771
+ functionName: 'remoteShare',
11772
+ eventPayload: {
11773
+ memberId: null,
11774
+ url,
11775
+ shareInstanceId,
11776
+ annotationInfo: undefined,
11777
+ resourceType: undefined,
11778
+ },
11779
+ }
11780
+ : {
11781
+ eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
11782
+ functionName: 'startWhiteboardShare',
11783
+ eventPayload: {resourceUrl, memberId: beneficiaryId},
11784
+ }
11785
+ );
11775
11786
 
11787
+ shareStatus = meeting.webinar.selfIsAttendee
11788
+ ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
11789
+ : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
11776
11790
  }
11777
11791
 
11778
11792
  if (eventTrigger.member) {
@@ -11804,24 +11818,29 @@ describe('plugin-meetings', () => {
11804
11818
  newPayload.current.content.disposition = FLOOR_ACTION.ACCEPTED;
11805
11819
  newPayload.current.content.beneficiaryId = otherBeneficiaryId;
11806
11820
 
11807
- eventTrigger.share.push(meeting.webinar.selfIsAttendee ? {
11808
- eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
11809
- functionName: 'remoteShare',
11810
- eventPayload: {
11811
- memberId: null,
11812
- url,
11813
- shareInstanceId,
11814
- annotationInfo: undefined,
11815
- resourceType: undefined,
11816
- },
11817
- } : {
11818
- eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
11819
- functionName: 'startWhiteboardShare',
11820
- eventPayload: {resourceUrl, memberId: beneficiaryId},
11821
- });
11822
-
11823
- shareStatus = meeting.webinar.selfIsAttendee ? SHARE_STATUS.REMOTE_SHARE_ACTIVE : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
11821
+ eventTrigger.share.push(
11822
+ meeting.webinar.selfIsAttendee
11823
+ ? {
11824
+ eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
11825
+ functionName: 'remoteShare',
11826
+ eventPayload: {
11827
+ memberId: null,
11828
+ url,
11829
+ shareInstanceId,
11830
+ annotationInfo: undefined,
11831
+ resourceType: undefined,
11832
+ },
11833
+ }
11834
+ : {
11835
+ eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
11836
+ functionName: 'startWhiteboardShare',
11837
+ eventPayload: {resourceUrl, memberId: beneficiaryId},
11838
+ }
11839
+ );
11824
11840
 
11841
+ shareStatus = meeting.webinar.selfIsAttendee
11842
+ ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
11843
+ : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
11825
11844
  } else {
11826
11845
  eventTrigger.share.push({
11827
11846
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
@@ -11951,24 +11970,24 @@ describe('plugin-meetings', () => {
11951
11970
  describe('Whiteboard Share - Webinar Attendee', () => {
11952
11971
  it('Scenario #1: Whiteboard sharing as a webinar attendee', () => {
11953
11972
  // Set the webinar attendee flag
11954
- meeting.webinar = { selfIsAttendee: true };
11973
+ meeting.webinar = {selfIsAttendee: true};
11955
11974
  meeting.locusInfo.info.isWebinar = true;
11956
11975
 
11957
11976
  // Step 1: Start sharing whiteboard A
11958
11977
  const data1 = generateData(
11959
- blankPayload, // Initial payload
11960
- true, // isGranting: Granting share
11961
- false, // isContent: Whiteboard (not content)
11962
- USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
11978
+ blankPayload, // Initial payload
11979
+ true, // isGranting: Granting share
11980
+ false, // isContent: Whiteboard (not content)
11981
+ USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
11963
11982
  RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
11964
11983
  );
11965
11984
 
11966
11985
  // Step 2: Stop sharing whiteboard A
11967
11986
  const data2 = generateData(
11968
- data1.payload, // Updated payload from Step 1
11969
- false, // isGranting: Stopping share
11970
- false, // isContent: Whiteboard
11971
- USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
11987
+ data1.payload, // Updated payload from Step 1
11988
+ false, // isGranting: Stopping share
11989
+ false, // isContent: Whiteboard
11990
+ USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
11972
11991
  );
11973
11992
 
11974
11993
  // Validate the payload changes and status updates
@@ -11979,7 +11998,6 @@ describe('plugin-meetings', () => {
11979
11998
  });
11980
11999
  });
11981
12000
 
11982
-
11983
12001
  describe('Whiteboard A --> Whiteboard B', () => {
11984
12002
  it('Scenario #1: you share both whiteboards', () => {
11985
12003
  const data1 = generateData(
@@ -13290,7 +13308,7 @@ describe('plugin-meetings', () => {
13290
13308
  await meeting.roapMessageReceived(fakeMessage);
13291
13309
 
13292
13310
  assert.fail('Expected MultistreamNotSupportedError to be thrown');
13293
- } catch(e) {
13311
+ } catch (e) {
13294
13312
  assert.isTrue(e instanceof MultistreamNotSupportedError);
13295
13313
  }
13296
13314
 
@@ -1,14 +1,18 @@
1
1
  import 'jsdom-global/register';
2
2
  import sinon from 'sinon';
3
3
  import {assert} from '@webex/test-helper-chai';
4
- import { cloneDeep } from 'lodash';
5
-
4
+ import {cloneDeep} from 'lodash';
5
+ import {EventEmitter} from 'events';
6
6
  import MockWebex from '@webex/test-helper-mock-webex';
7
7
  import Meetings from '@webex/plugin-meetings';
8
- import { LocalMuteRequest, LocusMediaRequest, RoapRequest } from "@webex/plugin-meetings/src/meeting/locusMediaRequest";
8
+ import {
9
+ LocalMuteRequest,
10
+ LocusMediaRequest,
11
+ RoapRequest,
12
+ } from '@webex/plugin-meetings/src/meeting/locusMediaRequest';
9
13
  import testUtils from '../../../utils/testUtils';
10
- import { Defer } from '@webex/common';
11
- import { IP_VERSION } from '../../../../src/constants';
14
+ import {Defer} from '@webex/common';
15
+ import {IP_VERSION} from '../../../../src/constants';
12
16
 
13
17
  describe('LocusMediaRequest.send()', () => {
14
18
  let locusMediaRequest: LocusMediaRequest;
@@ -16,10 +20,10 @@ describe('LocusMediaRequest.send()', () => {
16
20
  let mockWebex;
17
21
 
18
22
  const fakeLocusResponse = {
19
- locus: { something: 'whatever'}
23
+ locus: {something: 'whatever'},
20
24
  };
21
25
 
22
- const exampleRoapRequestBody:RoapRequest = {
26
+ const exampleRoapRequestBody: RoapRequest = {
23
27
  type: 'RoapMessage',
24
28
  mediaId: 'mediaId',
25
29
  selfUrl: 'fakeMeetingSelfUrl',
@@ -31,8 +35,11 @@ describe('LocusMediaRequest.send()', () => {
31
35
  tieBreaker: 0xfffffffe,
32
36
  },
33
37
  reachability: {
34
- 'wjfkm.wjfkm.*': {udp:{reachable: true}, tcp:{reachable:false}},
35
- '1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {udp:{reachable: false}, tcp:{reachable:true}},
38
+ 'wjfkm.wjfkm.*': {udp: {reachable: true}, tcp: {reachable: false}},
39
+ '1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {
40
+ udp: {reachable: false},
41
+ tcp: {reachable: true},
42
+ },
36
43
  },
37
44
  clientMediaPreferences: {
38
45
  preferTranscoding: false,
@@ -45,19 +52,22 @@ describe('LocusMediaRequest.send()', () => {
45
52
  reachability: {
46
53
  version: '1',
47
54
  result: 'some fake reachability result',
48
- }
49
- }
55
+ },
56
+ },
50
57
  };
51
58
 
52
- const createExpectedRoapBody = (expectedMessageType, expectedMute:{audioMuted: boolean, videoMuted: boolean}) => {
59
+ const createExpectedRoapBody = (
60
+ expectedMessageType,
61
+ expectedMute: {audioMuted: boolean; videoMuted: boolean}
62
+ ) => {
53
63
  return {
54
- device: { url: 'deviceUrl', deviceType: 'deviceType', regionCode: 'regionCode' },
64
+ device: {url: 'deviceUrl', deviceType: 'deviceType', regionCode: 'regionCode'},
55
65
  correlationId: 'correlationId',
56
66
  localMedias: [
57
67
  {
58
68
  localSdp: `{"audioMuted":${expectedMute.audioMuted},"videoMuted":${expectedMute.videoMuted},"roapMessage":{"messageType":"${expectedMessageType}","sdps":["sdp"],"version":"2","seq":1,"tieBreaker":4294967294},"reachability":{"wjfkm.wjfkm.*":{"udp":{"reachable":true},"tcp":{"reachable":false}},"1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*":{"udp":{"reachable":false},"tcp":{"reachable":true}}}}`,
59
- mediaId: 'mediaId'
60
- }
69
+ mediaId: 'mediaId',
70
+ },
61
71
  ],
62
72
  clientMediaPreferences: {
63
73
  preferTranscoding: false,
@@ -65,24 +75,27 @@ describe('LocusMediaRequest.send()', () => {
65
75
  joinCookie: {
66
76
  anycastEntryPoint: 'aws-eu-west-1',
67
77
  clientIpAddress: 'some ip',
68
- timeShot: '2023-05-23T08:03:49Z'
78
+ timeShot: '2023-05-23T08:03:49Z',
69
79
  },
70
80
  reachability: {
71
81
  version: '1',
72
82
  result: 'some fake reachability result',
73
- }
74
- }
83
+ },
84
+ },
75
85
  };
76
86
  };
77
87
 
78
- const exampleLocalMuteRequestBody:LocalMuteRequest = {
88
+ const exampleLocalMuteRequestBody: LocalMuteRequest = {
79
89
  type: 'LocalMute',
80
90
  mediaId: 'mediaId',
81
91
  selfUrl: 'fakeMeetingSelfUrl',
82
92
  muteOptions: {},
83
93
  };
84
94
 
85
- const createExpectedLocalMuteBody = (expectedMute:{audioMuted: boolean, videoMuted: boolean}, sequence = undefined) => {
95
+ const createExpectedLocalMuteBody = (
96
+ expectedMute: {audioMuted: boolean; videoMuted: boolean},
97
+ sequence = undefined
98
+ ) => {
86
99
  const body: any = {
87
100
  device: {
88
101
  url: 'deviceUrl',
@@ -116,33 +129,37 @@ describe('LocusMediaRequest.send()', () => {
116
129
 
117
130
  mockWebex.internal = {
118
131
  newMetrics: {
119
- submitClientEvent: sinon.stub()
132
+ submitClientEvent: sinon.stub(),
120
133
  },
121
134
  };
122
135
 
123
- locusMediaRequest = new LocusMediaRequest({
124
- device: {
125
- url: 'deviceUrl',
126
- deviceType: 'deviceType',
127
- regionCode: 'regionCode',
136
+ locusMediaRequest = new LocusMediaRequest(
137
+ {
138
+ device: {
139
+ url: 'deviceUrl',
140
+ deviceType: 'deviceType',
141
+ regionCode: 'regionCode',
142
+ },
143
+ correlationId: 'correlationId',
144
+ meetingId: 'meetingId',
145
+ preferTranscoding: true,
128
146
  },
129
- correlationId: 'correlationId',
130
- meetingId: 'meetingId',
131
- preferTranscoding: true,
132
- }, {
133
- parent: mockWebex,
134
- });
147
+ {
148
+ parent: mockWebex,
149
+ }
150
+ );
135
151
  webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
136
- })
152
+ });
137
153
 
138
- const sendLocalMute = (muteOptions, overrides={}) => locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
154
+ const sendLocalMute = (muteOptions, overrides = {}) =>
155
+ locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
139
156
 
140
157
  const sendRoapMessage = (messageType) => {
141
158
  const request = cloneDeep(exampleRoapRequestBody);
142
159
 
143
160
  request.roapMessage.messageType = messageType;
144
161
  return locusMediaRequest.send(request);
145
- }
162
+ };
146
163
 
147
164
  /** Helper function that makes sure the LocusMediaRequest.confluenceState is 'created' */
148
165
  const ensureConfluenceCreated = async () => {
@@ -150,7 +167,7 @@ describe('LocusMediaRequest.send()', () => {
150
167
 
151
168
  webexRequestStub.resetHistory();
152
169
  mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
153
- }
170
+ };
154
171
 
155
172
  const checkMetrics = (expectedMetrics: boolean = true) => {
156
173
  if (expectedMetrics) {
@@ -170,7 +187,7 @@ describe('LocusMediaRequest.send()', () => {
170
187
  } else {
171
188
  assert.notCalled(mockWebex.internal.newMetrics.submitClientEvent);
172
189
  }
173
- }
190
+ };
174
191
 
175
192
  it('sends a roap message', async () => {
176
193
  const result = await sendRoapMessage('OFFER');
@@ -181,6 +198,8 @@ describe('LocusMediaRequest.send()', () => {
181
198
  method: 'PUT',
182
199
  uri: 'fakeMeetingSelfUrl/media',
183
200
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
201
+ upload: sinon.match.instanceOf(EventEmitter),
202
+ download: sinon.match.instanceOf(EventEmitter),
184
203
  });
185
204
 
186
205
  checkMetrics();
@@ -210,6 +229,8 @@ describe('LocusMediaRequest.send()', () => {
210
229
  method: 'PUT',
211
230
  uri: 'fakeMeetingSelfUrl/media',
212
231
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}),
232
+ upload: sinon.match.instanceOf(EventEmitter),
233
+ download: sinon.match.instanceOf(EventEmitter),
213
234
  });
214
235
 
215
236
  checkMetrics(false);
@@ -228,6 +249,8 @@ describe('LocusMediaRequest.send()', () => {
228
249
  method: 'PUT',
229
250
  uri: 'fakeMeetingSelfUrl/media',
230
251
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}, sequence),
252
+ upload: sinon.match.instanceOf(EventEmitter),
253
+ download: sinon.match.instanceOf(EventEmitter),
231
254
  });
232
255
  });
233
256
 
@@ -237,15 +260,13 @@ describe('LocusMediaRequest.send()', () => {
237
260
  let result1;
238
261
  let result2;
239
262
 
240
- const promise1 = sendLocalMute({audioMuted: true, videoMuted: false})
241
- .then((result) => {
242
- result1 = result;
243
- });
263
+ const promise1 = sendLocalMute({audioMuted: true, videoMuted: false}).then((result) => {
264
+ result1 = result;
265
+ });
244
266
 
245
- const promise2 = sendLocalMute({audioMuted: false, videoMuted: true})
246
- .then((result) => {
247
- result2 = result;
248
- });
267
+ const promise2 = sendLocalMute({audioMuted: false, videoMuted: true}).then((result) => {
268
+ result2 = result;
269
+ });
249
270
 
250
271
  await testUtils.flushPromises();
251
272
 
@@ -258,6 +279,8 @@ describe('LocusMediaRequest.send()', () => {
258
279
  method: 'PUT',
259
280
  uri: 'fakeMeetingSelfUrl/media',
260
281
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
282
+ upload: sinon.match.instanceOf(EventEmitter),
283
+ download: sinon.match.instanceOf(EventEmitter),
261
284
  });
262
285
 
263
286
  checkMetrics(false);
@@ -277,6 +300,8 @@ describe('LocusMediaRequest.send()', () => {
277
300
  method: 'PUT',
278
301
  uri: 'fakeMeetingSelfUrl/media',
279
302
  body: createExpectedLocalMuteBody({audioMuted: true, videoMuted: false}),
303
+ upload: sinon.match.instanceOf(EventEmitter),
304
+ download: sinon.match.instanceOf(EventEmitter),
280
305
  });
281
306
 
282
307
  checkMetrics(false);
@@ -296,6 +321,8 @@ describe('LocusMediaRequest.send()', () => {
296
321
  method: 'PUT',
297
322
  uri: 'fakeMeetingSelfUrl/media',
298
323
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: false}),
324
+ upload: sinon.match.instanceOf(EventEmitter),
325
+ download: sinon.match.instanceOf(EventEmitter),
299
326
  });
300
327
 
301
328
  checkMetrics();
@@ -328,10 +355,10 @@ describe('LocusMediaRequest.send()', () => {
328
355
  * after the processing cycle from which it was called is finished.
329
356
  * This helper function waits for this to happen - it's needed, because we're using
330
357
  * fake timers in these tests
331
- */
358
+ */
332
359
  const ensureQueueProcessingIsStarted = () => {
333
360
  clock.tick(1);
334
- }
361
+ };
335
362
  it('queues requests if there is one already in progress', async () => {
336
363
  results.push(sendRoapMessage('OFFER'));
337
364
 
@@ -342,6 +369,8 @@ describe('LocusMediaRequest.send()', () => {
342
369
  method: 'PUT',
343
370
  uri: 'fakeMeetingSelfUrl/media',
344
371
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
372
+ upload: sinon.match.instanceOf(EventEmitter),
373
+ download: sinon.match.instanceOf(EventEmitter),
345
374
  });
346
375
 
347
376
  webexRequestStub.resetHistory();
@@ -364,6 +393,8 @@ describe('LocusMediaRequest.send()', () => {
364
393
  method: 'PUT',
365
394
  uri: 'fakeMeetingSelfUrl/media',
366
395
  body: createExpectedRoapBody('OK', {audioMuted: true, videoMuted: true}),
396
+ upload: sinon.match.instanceOf(EventEmitter),
397
+ download: sinon.match.instanceOf(EventEmitter),
367
398
  });
368
399
 
369
400
  // promise returned by the first call to send OFFER should be resolved by now
@@ -386,6 +417,8 @@ describe('LocusMediaRequest.send()', () => {
386
417
  method: 'PUT',
387
418
  uri: 'fakeMeetingSelfUrl/media',
388
419
  body: createExpectedRoapBody('OFFER', {audioMuted: false, videoMuted: false}),
420
+ upload: sinon.match.instanceOf(EventEmitter),
421
+ download: sinon.match.instanceOf(EventEmitter),
389
422
  });
390
423
 
391
424
  webexRequestStub.resetHistory();
@@ -410,6 +443,8 @@ describe('LocusMediaRequest.send()', () => {
410
443
  method: 'PUT',
411
444
  uri: 'fakeMeetingSelfUrl/media',
412
445
  body: createExpectedRoapBody('OK', {audioMuted: false, videoMuted: true}),
446
+ upload: sinon.match.instanceOf(EventEmitter),
447
+ download: sinon.match.instanceOf(EventEmitter),
413
448
  });
414
449
 
415
450
  // promise returned by the first call to send OFFER should be resolved by now
@@ -438,16 +473,17 @@ describe('LocusMediaRequest.send()', () => {
438
473
 
439
474
  ensureQueueProcessingIsStarted();
440
475
 
441
- sendLocalMute({audioMuted: false, videoMuted: true})
442
- .then((response) => {
443
- result = response;
444
- });
476
+ sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
477
+ result = response;
478
+ });
445
479
 
446
480
  // only roap offer should have been sent so far
447
481
  assert.calledOnceWithExactly(webexRequestStub, {
448
482
  method: 'PUT',
449
483
  uri: 'fakeMeetingSelfUrl/media',
450
484
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
485
+ upload: sinon.match.instanceOf(EventEmitter),
486
+ download: sinon.match.instanceOf(EventEmitter),
451
487
  });
452
488
  assert.equal(result, undefined); // sendLocalMute shouldn't resolve yet, as the request should be queued
453
489
  assert.equal(locusMediaRequest.isConfluenceCreated(), false);
@@ -464,10 +500,12 @@ describe('LocusMediaRequest.send()', () => {
464
500
  method: 'PUT',
465
501
  uri: 'fakeMeetingSelfUrl/media',
466
502
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
503
+ upload: sinon.match.instanceOf(EventEmitter),
504
+ download: sinon.match.instanceOf(EventEmitter),
467
505
  });
468
506
 
469
507
  // check also the result once Locus replies to local mute
470
- const fakeLocusResponse = { response: 'ok'};
508
+ const fakeLocusResponse = {response: 'ok'};
471
509
  requestsToLocus[1].resolve(fakeLocusResponse);
472
510
  await testUtils.flushPromises();
473
511
  assert.deepEqual(result, fakeLocusResponse);
@@ -487,10 +525,9 @@ describe('LocusMediaRequest.send()', () => {
487
525
  assert.equal(locusMediaRequest.isConfluenceCreated(), true);
488
526
 
489
527
  // now send local mute
490
- sendLocalMute({audioMuted: false, videoMuted: true})
491
- .then((response) => {
492
- result = response;
493
- });
528
+ sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
529
+ result = response;
530
+ });
494
531
 
495
532
  ensureQueueProcessingIsStarted();
496
533
 
@@ -499,8 +536,9 @@ describe('LocusMediaRequest.send()', () => {
499
536
  method: 'PUT',
500
537
  uri: 'fakeMeetingSelfUrl/media',
501
538
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
539
+ upload: sinon.match.instanceOf(EventEmitter),
540
+ download: sinon.match.instanceOf(EventEmitter),
502
541
  });
503
542
  });
504
-
505
543
  });
506
- })
544
+ });