@webex/plugin-meetings 2.59.1 → 2.59.3-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/.eslintrc.js +6 -6
  2. package/LICENSE +1 -1
  3. package/README.md +1204 -1204
  4. package/UPGRADING.md +287 -287
  5. package/babel.config.js +3 -3
  6. package/browsers.js +108 -108
  7. package/dist/common/browser-detection.d.ts +9 -9
  8. package/dist/common/browser-detection.js.map +1 -1
  9. package/dist/common/collection.d.ts +48 -48
  10. package/dist/common/collection.js +43 -43
  11. package/dist/common/collection.js.map +1 -1
  12. package/dist/common/config.d.ts +2 -2
  13. package/dist/common/config.js.map +1 -1
  14. package/dist/common/errors/captcha-error.d.ts +15 -15
  15. package/dist/common/errors/captcha-error.js +7 -7
  16. package/dist/common/errors/captcha-error.js.map +1 -1
  17. package/dist/common/errors/intent-to-join.d.ts +16 -16
  18. package/dist/common/errors/intent-to-join.js +7 -7
  19. package/dist/common/errors/intent-to-join.js.map +1 -1
  20. package/dist/common/errors/join-meeting.d.ts +17 -17
  21. package/dist/common/errors/join-meeting.js +8 -8
  22. package/dist/common/errors/join-meeting.js.map +1 -1
  23. package/dist/common/errors/media.d.ts +15 -15
  24. package/dist/common/errors/media.js +7 -7
  25. package/dist/common/errors/media.js.map +1 -1
  26. package/dist/common/errors/parameter.d.ts +15 -15
  27. package/dist/common/errors/parameter.js +7 -7
  28. package/dist/common/errors/parameter.js.map +1 -1
  29. package/dist/common/errors/password-error.d.ts +15 -15
  30. package/dist/common/errors/password-error.js +7 -7
  31. package/dist/common/errors/password-error.js.map +1 -1
  32. package/dist/common/errors/permission.d.ts +14 -14
  33. package/dist/common/errors/permission.js +6 -6
  34. package/dist/common/errors/permission.js.map +1 -1
  35. package/dist/common/errors/reconnection-in-progress.d.ts +9 -9
  36. package/dist/common/errors/reconnection-in-progress.js +6 -6
  37. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  38. package/dist/common/errors/reconnection.d.ts +15 -15
  39. package/dist/common/errors/reconnection.js +7 -7
  40. package/dist/common/errors/reconnection.js.map +1 -1
  41. package/dist/common/errors/stats.d.ts +15 -15
  42. package/dist/common/errors/stats.js +7 -7
  43. package/dist/common/errors/stats.js.map +1 -1
  44. package/dist/common/errors/webex-errors.d.ts +81 -81
  45. package/dist/common/errors/webex-errors.js +42 -42
  46. package/dist/common/errors/webex-errors.js.map +1 -1
  47. package/dist/common/errors/webex-meetings-error.d.ts +20 -20
  48. package/dist/common/errors/webex-meetings-error.js +12 -12
  49. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  50. package/dist/common/events/events-scope.d.ts +17 -17
  51. package/dist/common/events/events-scope.js +10 -10
  52. package/dist/common/events/events-scope.js.map +1 -1
  53. package/dist/common/events/events.d.ts +12 -12
  54. package/dist/common/events/events.js +4 -4
  55. package/dist/common/events/events.js.map +1 -1
  56. package/dist/common/events/trigger-proxy.d.ts +2 -2
  57. package/dist/common/events/trigger-proxy.js.map +1 -1
  58. package/dist/common/events/util.d.ts +2 -2
  59. package/dist/common/events/util.js.map +1 -1
  60. package/dist/common/logs/logger-config.d.ts +2 -2
  61. package/dist/common/logs/logger-config.js.map +1 -1
  62. package/dist/common/logs/logger-proxy.d.ts +2 -2
  63. package/dist/common/logs/logger-proxy.js.map +1 -1
  64. package/dist/common/logs/request.d.ts +34 -34
  65. package/dist/common/logs/request.js +18 -18
  66. package/dist/common/logs/request.js.map +1 -1
  67. package/dist/common/queue.d.ts +32 -32
  68. package/dist/common/queue.js +18 -18
  69. package/dist/common/queue.js.map +1 -1
  70. package/dist/config.d.ts +73 -73
  71. package/dist/config.js.map +1 -1
  72. package/dist/constants.d.ts +924 -924
  73. package/dist/constants.js +9 -9
  74. package/dist/constants.js.map +1 -1
  75. package/dist/controls-options-manager/constants.d.ts +4 -4
  76. package/dist/controls-options-manager/constants.js.map +1 -1
  77. package/dist/controls-options-manager/enums.d.ts +5 -5
  78. package/dist/controls-options-manager/enums.js.map +1 -1
  79. package/dist/controls-options-manager/index.d.ts +120 -120
  80. package/dist/controls-options-manager/index.js +81 -81
  81. package/dist/controls-options-manager/index.js.map +1 -1
  82. package/dist/controls-options-manager/util.d.ts +7 -7
  83. package/dist/controls-options-manager/util.js.map +1 -1
  84. package/dist/index.d.ts +4 -4
  85. package/dist/index.js.map +1 -1
  86. package/dist/locus-info/controlsUtils.d.ts +2 -2
  87. package/dist/locus-info/controlsUtils.js +21 -21
  88. package/dist/locus-info/controlsUtils.js.map +1 -1
  89. package/dist/locus-info/embeddedAppsUtils.d.ts +2 -2
  90. package/dist/locus-info/embeddedAppsUtils.js +14 -14
  91. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  92. package/dist/locus-info/fullState.d.ts +2 -2
  93. package/dist/locus-info/fullState.js.map +1 -1
  94. package/dist/locus-info/hostUtils.d.ts +2 -2
  95. package/dist/locus-info/hostUtils.js +19 -19
  96. package/dist/locus-info/hostUtils.js.map +1 -1
  97. package/dist/locus-info/index.d.ts +269 -269
  98. package/dist/locus-info/index.js +180 -180
  99. package/dist/locus-info/index.js.map +1 -1
  100. package/dist/locus-info/infoUtils.d.ts +2 -2
  101. package/dist/locus-info/infoUtils.js.map +1 -1
  102. package/dist/locus-info/mediaSharesUtils.d.ts +2 -2
  103. package/dist/locus-info/mediaSharesUtils.js +50 -50
  104. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  105. package/dist/locus-info/parser.d.ts +212 -212
  106. package/dist/locus-info/parser.js +136 -136
  107. package/dist/locus-info/parser.js.map +1 -1
  108. package/dist/locus-info/selfUtils.d.ts +2 -2
  109. package/dist/locus-info/selfUtils.js +52 -52
  110. package/dist/locus-info/selfUtils.js.map +1 -1
  111. package/dist/media/index.d.ts +32 -32
  112. package/dist/media/index.js +145 -145
  113. package/dist/media/index.js.map +1 -1
  114. package/dist/media/properties.d.ts +108 -108
  115. package/dist/media/properties.js +49 -49
  116. package/dist/media/properties.js.map +1 -1
  117. package/dist/media/util.d.ts +2 -2
  118. package/dist/media/util.js.map +1 -1
  119. package/dist/mediaQualityMetrics/config.d.ts +233 -233
  120. package/dist/mediaQualityMetrics/config.js.map +1 -1
  121. package/dist/meeting/effectsState.d.ts +42 -42
  122. package/dist/meeting/effectsState.js +24 -24
  123. package/dist/meeting/effectsState.js.map +1 -1
  124. package/dist/meeting/in-meeting-actions.d.ts +79 -79
  125. package/dist/meeting/in-meeting-actions.js +11 -11
  126. package/dist/meeting/in-meeting-actions.js.map +1 -1
  127. package/dist/meeting/index.d.ts +1621 -1621
  128. package/dist/meeting/index.js +1502 -1505
  129. package/dist/meeting/index.js.map +1 -1
  130. package/dist/meeting/muteState.d.ts +116 -116
  131. package/dist/meeting/muteState.js +85 -85
  132. package/dist/meeting/muteState.js.map +1 -1
  133. package/dist/meeting/request.d.ts +255 -255
  134. package/dist/meeting/request.js +141 -141
  135. package/dist/meeting/request.js.map +1 -1
  136. package/dist/meeting/state.d.ts +9 -9
  137. package/dist/meeting/state.js +30 -30
  138. package/dist/meeting/state.js.map +1 -1
  139. package/dist/meeting/util.d.ts +2 -2
  140. package/dist/meeting/util.js.map +1 -1
  141. package/dist/meeting-info/collection.d.ts +20 -20
  142. package/dist/meeting-info/collection.js +11 -11
  143. package/dist/meeting-info/collection.js.map +1 -1
  144. package/dist/meeting-info/index.d.ts +57 -57
  145. package/dist/meeting-info/index.js +50 -50
  146. package/dist/meeting-info/index.js.map +1 -1
  147. package/dist/meeting-info/meeting-info-v2.d.ts +93 -93
  148. package/dist/meeting-info/meeting-info-v2.js +52 -52
  149. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  150. package/dist/meeting-info/request.d.ts +22 -22
  151. package/dist/meeting-info/request.js +14 -14
  152. package/dist/meeting-info/request.js.map +1 -1
  153. package/dist/meeting-info/util.d.ts +2 -2
  154. package/dist/meeting-info/util.js +9 -9
  155. package/dist/meeting-info/util.js.map +1 -1
  156. package/dist/meeting-info/utilv2.d.ts +2 -2
  157. package/dist/meeting-info/utilv2.js +20 -20
  158. package/dist/meeting-info/utilv2.js.map +1 -1
  159. package/dist/meetings/collection.d.ts +23 -23
  160. package/dist/meetings/collection.js +14 -14
  161. package/dist/meetings/collection.js.map +1 -1
  162. package/dist/meetings/index.d.ts +296 -296
  163. package/dist/meetings/index.js +259 -259
  164. package/dist/meetings/index.js.map +1 -1
  165. package/dist/meetings/request.d.ts +27 -27
  166. package/dist/meetings/request.js +15 -15
  167. package/dist/meetings/request.js.map +1 -1
  168. package/dist/meetings/util.d.ts +18 -18
  169. package/dist/meetings/util.js +29 -29
  170. package/dist/meetings/util.js.map +1 -1
  171. package/dist/member/index.d.ts +147 -147
  172. package/dist/member/index.js +214 -214
  173. package/dist/member/index.js.map +1 -1
  174. package/dist/member/member.types.d.ts +11 -11
  175. package/dist/member/member.types.js.map +1 -1
  176. package/dist/member/util.d.ts +2 -2
  177. package/dist/member/util.js +60 -60
  178. package/dist/member/util.js.map +1 -1
  179. package/dist/members/collection.d.ts +24 -24
  180. package/dist/members/collection.js +11 -11
  181. package/dist/members/collection.js.map +1 -1
  182. package/dist/members/index.d.ts +298 -298
  183. package/dist/members/index.js +275 -275
  184. package/dist/members/index.js.map +1 -1
  185. package/dist/members/request.d.ts +50 -50
  186. package/dist/members/request.js +27 -27
  187. package/dist/members/request.js.map +1 -1
  188. package/dist/members/util.d.ts +2 -2
  189. package/dist/members/util.js +21 -21
  190. package/dist/members/util.js.map +1 -1
  191. package/dist/metrics/config.d.ts +169 -169
  192. package/dist/metrics/config.js.map +1 -1
  193. package/dist/metrics/constants.d.ts +57 -57
  194. package/dist/metrics/constants.js.map +1 -1
  195. package/dist/metrics/index.d.ts +152 -152
  196. package/dist/metrics/index.js +90 -90
  197. package/dist/metrics/index.js.map +1 -1
  198. package/dist/networkQualityMonitor/index.d.ts +70 -70
  199. package/dist/networkQualityMonitor/index.js +65 -65
  200. package/dist/networkQualityMonitor/index.js.map +1 -1
  201. package/dist/peer-connection-manager/index.d.ts +6 -6
  202. package/dist/peer-connection-manager/index.js +87 -87
  203. package/dist/peer-connection-manager/index.js.map +1 -1
  204. package/dist/peer-connection-manager/util.d.ts +6 -6
  205. package/dist/peer-connection-manager/util.js +9 -9
  206. package/dist/peer-connection-manager/util.js.map +1 -1
  207. package/dist/personal-meeting-room/index.d.ts +47 -47
  208. package/dist/personal-meeting-room/index.js +67 -67
  209. package/dist/personal-meeting-room/index.js.map +1 -1
  210. package/dist/personal-meeting-room/request.d.ts +14 -14
  211. package/dist/personal-meeting-room/request.js +7 -7
  212. package/dist/personal-meeting-room/request.js.map +1 -1
  213. package/dist/personal-meeting-room/util.d.ts +2 -2
  214. package/dist/personal-meeting-room/util.js.map +1 -1
  215. package/dist/reachability/index.d.ts +139 -139
  216. package/dist/reachability/index.js +110 -110
  217. package/dist/reachability/index.js.map +1 -1
  218. package/dist/reachability/request.d.ts +35 -35
  219. package/dist/reachability/request.js +15 -15
  220. package/dist/reachability/request.js.map +1 -1
  221. package/dist/reactions/reactions.d.ts +4 -4
  222. package/dist/reactions/reactions.js.map +1 -1
  223. package/dist/reactions/reactions.type.d.ts +32 -32
  224. package/dist/reactions/reactions.type.js.map +1 -1
  225. package/dist/reconnection-manager/index.d.ts +112 -112
  226. package/dist/reconnection-manager/index.js +112 -112
  227. package/dist/reconnection-manager/index.js.map +1 -1
  228. package/dist/recording-controller/enums.d.ts +7 -7
  229. package/dist/recording-controller/enums.js.map +1 -1
  230. package/dist/recording-controller/index.d.ts +193 -193
  231. package/dist/recording-controller/index.js +127 -127
  232. package/dist/recording-controller/index.js.map +1 -1
  233. package/dist/recording-controller/util.d.ts +13 -13
  234. package/dist/recording-controller/util.js.map +1 -1
  235. package/dist/roap/collection.d.ts +10 -10
  236. package/dist/roap/collection.js.map +1 -1
  237. package/dist/roap/handler.d.ts +47 -47
  238. package/dist/roap/handler.js +27 -27
  239. package/dist/roap/handler.js.map +1 -1
  240. package/dist/roap/index.d.ts +116 -116
  241. package/dist/roap/index.js +111 -111
  242. package/dist/roap/index.js.map +1 -1
  243. package/dist/roap/request.d.ts +35 -35
  244. package/dist/roap/request.js +17 -17
  245. package/dist/roap/request.js.map +1 -1
  246. package/dist/roap/state.d.ts +9 -9
  247. package/dist/roap/state.js +14 -14
  248. package/dist/roap/state.js.map +1 -1
  249. package/dist/roap/turnDiscovery.d.ts +67 -67
  250. package/dist/roap/turnDiscovery.js +46 -46
  251. package/dist/roap/turnDiscovery.js.map +1 -1
  252. package/dist/roap/util.d.ts +2 -2
  253. package/dist/roap/util.js.map +1 -1
  254. package/dist/statsAnalyzer/global.d.ts +126 -126
  255. package/dist/statsAnalyzer/global.js.map +1 -1
  256. package/dist/statsAnalyzer/index.d.ts +190 -190
  257. package/dist/statsAnalyzer/index.js +128 -128
  258. package/dist/statsAnalyzer/index.js.map +1 -1
  259. package/dist/statsAnalyzer/mqaUtil.d.ts +22 -22
  260. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  261. package/dist/transcription/index.d.ts +64 -64
  262. package/dist/transcription/index.js +42 -42
  263. package/dist/transcription/index.js.map +1 -1
  264. package/internal-README.md +172 -172
  265. package/jest.config.js +3 -3
  266. package/package.json +22 -21
  267. package/process +1 -1
  268. package/src/common/browser-detection.ts +39 -39
  269. package/src/common/collection.ts +94 -94
  270. package/src/common/config.ts +9 -9
  271. package/src/common/errors/captcha-error.ts +25 -25
  272. package/src/common/errors/intent-to-join.ts +27 -27
  273. package/src/common/errors/join-meeting.ts +32 -32
  274. package/src/common/errors/media.ts +25 -25
  275. package/src/common/errors/parameter.ts +33 -33
  276. package/src/common/errors/password-error.ts +25 -25
  277. package/src/common/errors/permission.ts +24 -24
  278. package/src/common/errors/reconnection-in-progress.ts +8 -8
  279. package/src/common/errors/reconnection.ts +25 -25
  280. package/src/common/errors/stats.ts +25 -25
  281. package/src/common/errors/webex-errors.ts +140 -140
  282. package/src/common/errors/webex-meetings-error.ts +35 -35
  283. package/src/common/events/events-scope.ts +30 -30
  284. package/src/common/events/events.ts +25 -25
  285. package/src/common/events/trigger-proxy.ts +25 -25
  286. package/src/common/events/util.ts +39 -39
  287. package/src/common/logs/logger-config.ts +8 -8
  288. package/src/common/logs/logger-proxy.ts +44 -44
  289. package/src/common/logs/request.ts +65 -65
  290. package/src/common/queue.ts +50 -50
  291. package/src/config.ts +96 -96
  292. package/src/constants.ts +1121 -1121
  293. package/src/controls-options-manager/constants.ts +5 -5
  294. package/src/controls-options-manager/enums.ts +6 -6
  295. package/src/controls-options-manager/index.ts +183 -183
  296. package/src/controls-options-manager/util.ts +20 -20
  297. package/src/index.js +15 -15
  298. package/src/locus-info/controlsUtils.ts +112 -112
  299. package/src/locus-info/embeddedAppsUtils.ts +57 -57
  300. package/src/locus-info/fullState.ts +69 -69
  301. package/src/locus-info/hostUtils.ts +60 -60
  302. package/src/locus-info/index.ts +1303 -1303
  303. package/src/locus-info/infoUtils.ts +101 -101
  304. package/src/locus-info/mediaSharesUtils.ts +173 -173
  305. package/src/locus-info/parser.ts +680 -680
  306. package/src/locus-info/selfUtils.ts +428 -428
  307. package/src/media/index.ts +675 -675
  308. package/src/media/properties.ts +313 -313
  309. package/src/media/util.ts +37 -37
  310. package/src/mediaQualityMetrics/config.ts +382 -382
  311. package/src/meeting/effectsState.ts +209 -209
  312. package/src/meeting/in-meeting-actions.ts +153 -153
  313. package/src/meeting/index.ts +6537 -6543
  314. package/src/meeting/muteState.ts +365 -365
  315. package/src/meeting/request.ts +810 -810
  316. package/src/meeting/state.ts +194 -194
  317. package/src/meeting/util.ts +530 -530
  318. package/src/meeting-info/collection.ts +41 -41
  319. package/src/meeting-info/index.ts +137 -137
  320. package/src/meeting-info/meeting-info-v2.ts +273 -273
  321. package/src/meeting-info/request.ts +46 -46
  322. package/src/meeting-info/util.ts +314 -314
  323. package/src/meeting-info/utilv2.ts +324 -324
  324. package/src/meetings/collection.ts +43 -43
  325. package/src/meetings/index.ts +1128 -1128
  326. package/src/meetings/request.ts +81 -81
  327. package/src/meetings/util.ts +181 -181
  328. package/src/member/index.ts +446 -446
  329. package/src/member/member.types.ts +13 -13
  330. package/src/member/util.ts +286 -286
  331. package/src/members/collection.ts +40 -40
  332. package/src/members/index.ts +900 -900
  333. package/src/members/request.ts +175 -175
  334. package/src/members/util.ts +260 -260
  335. package/src/metrics/config.ts +485 -485
  336. package/src/metrics/constants.ts +61 -61
  337. package/src/metrics/index.ts +543 -543
  338. package/src/networkQualityMonitor/index.ts +211 -211
  339. package/src/peer-connection-manager/index.ts +847 -847
  340. package/src/peer-connection-manager/util.ts +119 -119
  341. package/src/personal-meeting-room/index.ts +157 -157
  342. package/src/personal-meeting-room/request.ts +48 -48
  343. package/src/personal-meeting-room/util.ts +49 -49
  344. package/src/reachability/index.ts +478 -478
  345. package/src/reachability/request.ts +81 -81
  346. package/src/reactions/reactions.ts +104 -104
  347. package/src/reactions/reactions.type.ts +36 -36
  348. package/src/reconnection-manager/index.ts +622 -622
  349. package/src/recording-controller/enums.ts +8 -8
  350. package/src/recording-controller/index.ts +315 -315
  351. package/src/recording-controller/util.ts +58 -58
  352. package/src/roap/collection.ts +62 -62
  353. package/src/roap/handler.ts +294 -294
  354. package/src/roap/index.ts +413 -413
  355. package/src/roap/request.ts +229 -229
  356. package/src/roap/state.ts +156 -156
  357. package/src/roap/turnDiscovery.ts +283 -283
  358. package/src/roap/util.ts +100 -100
  359. package/src/statsAnalyzer/global.ts +128 -128
  360. package/src/statsAnalyzer/index.ts +1266 -1266
  361. package/src/statsAnalyzer/mqaUtil.ts +290 -290
  362. package/src/transcription/index.ts +154 -154
  363. package/test/integration/spec/journey.js +941 -941
  364. package/test/integration/spec/space-meeting.js +457 -457
  365. package/test/integration/spec/transcription.js +55 -55
  366. package/test/unit/spec/common/browser-detection.js +119 -119
  367. package/test/unit/spec/common/queue.js +69 -69
  368. package/test/unit/spec/controls-options-manager/index.js +123 -123
  369. package/test/unit/spec/controls-options-manager/util.js +65 -65
  370. package/test/unit/spec/fixture/locus.js +406 -406
  371. package/test/unit/spec/locus-info/controlsUtils.js +82 -82
  372. package/test/unit/spec/locus-info/embeddedAppsUtils.js +104 -104
  373. package/test/unit/spec/locus-info/index.js +1272 -1272
  374. package/test/unit/spec/locus-info/infoUtils.js +138 -138
  375. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +975 -975
  376. package/test/unit/spec/locus-info/lib/SeqCmp.json +522 -522
  377. package/test/unit/spec/locus-info/lib/selfConstant.js +286 -286
  378. package/test/unit/spec/locus-info/parser.js +298 -298
  379. package/test/unit/spec/locus-info/selfUtils.js +185 -185
  380. package/test/unit/spec/media/properties.ts +305 -305
  381. package/test/unit/spec/meeting/effectsState.js +281 -281
  382. package/test/unit/spec/meeting/in-meeting-actions.ts +90 -90
  383. package/test/unit/spec/meeting/index.js +5227 -5227
  384. package/test/unit/spec/meeting/muteState.js +430 -430
  385. package/test/unit/spec/meeting/request.js +317 -317
  386. package/test/unit/spec/meeting/utils.js +319 -319
  387. package/test/unit/spec/meeting-info/meetinginfov2.js +376 -376
  388. package/test/unit/spec/meeting-info/request.js +64 -64
  389. package/test/unit/spec/meeting-info/util.js +37 -37
  390. package/test/unit/spec/meeting-info/utilv2.js +330 -330
  391. package/test/unit/spec/meetings/collection.js +52 -52
  392. package/test/unit/spec/meetings/index.js +1375 -1375
  393. package/test/unit/spec/meetings/utils.js +66 -66
  394. package/test/unit/spec/member/index.js +47 -47
  395. package/test/unit/spec/member/util.js +80 -80
  396. package/test/unit/spec/members/index.js +364 -364
  397. package/test/unit/spec/members/request.js +200 -200
  398. package/test/unit/spec/members/utils.js +42 -42
  399. package/test/unit/spec/metrics/index.js +111 -111
  400. package/test/unit/spec/networkQualityMonitor/index.js +99 -99
  401. package/test/unit/spec/peerconnection-manager/index.js +218 -218
  402. package/test/unit/spec/peerconnection-manager/utils.js +49 -49
  403. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +388 -388
  404. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +29 -29
  405. package/test/unit/spec/reachability/index.ts +50 -50
  406. package/test/unit/spec/reconnection-manager/index.js +206 -206
  407. package/test/unit/spec/recording-controller/index.js +230 -230
  408. package/test/unit/spec/recording-controller/util.js +101 -101
  409. package/test/unit/spec/roap/index.ts +128 -128
  410. package/test/unit/spec/roap/turnDiscovery.ts +372 -372
  411. package/test/unit/spec/roap/util.js +30 -30
  412. package/test/unit/spec/stats-analyzer/index.js +287 -287
  413. package/test/utils/cmr.js +104 -104
  414. package/test/utils/testUtils.js +287 -287
  415. package/test/utils/webex-config.js +77 -77
  416. package/test/utils/webex-test-users.js +82 -82
  417. package/tsconfig.json +5 -5
@@ -1,1128 +1,1128 @@
1
- /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
2
-
3
- import '@webex/internal-plugin-mercury';
4
- // @ts-ignore
5
- import {WebexPlugin} from '@webex/webex-core';
6
-
7
- import 'webrtc-adapter';
8
-
9
- import Metrics from '../metrics';
10
- import {trigger, eventType} from '../metrics/config';
11
- import LoggerConfig from '../common/logs/logger-config';
12
- import StaticConfig from '../common/config';
13
- import LoggerProxy from '../common/logs/logger-proxy';
14
- import LoggerRequest from '../common/logs/request';
15
- import Trigger from '../common/events/trigger-proxy';
16
- import Media from '../media';
17
- import MeetingUtil from '../meeting/util';
18
- import {
19
- MEETINGS,
20
- EVENTS,
21
- EVENT_TRIGGERS,
22
- READY,
23
- LOCUSEVENT,
24
- LOCUS_URL,
25
- MAX_RANDOM_DELAY_FOR_MEETING_INFO,
26
- ROAP,
27
- ONLINE,
28
- OFFLINE,
29
- _MEETING_,
30
- _JOIN_,
31
- _LOCUS_ID_,
32
- _INCOMING_,
33
- LOCUS,
34
- CORRELATION_ID,
35
- SIP_URI,
36
- _LEFT_,
37
- _ID_,
38
- MEETING_REMOVED_REASON,
39
- _CONVERSATION_URL_,
40
- CONVERSATION_URL,
41
- } from '../constants';
42
- import BEHAVIORAL_METRICS from '../metrics/constants';
43
- import MeetingInfo from '../meeting-info';
44
- import MeetingInfoV2 from '../meeting-info/meeting-info-v2';
45
- import Meeting from '../meeting';
46
- import PersonalMeetingRoom from '../personal-meeting-room';
47
- import Reachability from '../reachability';
48
- import Request from './request';
49
- import PasswordError from '../common/errors/password-error';
50
- import CaptchaError from '../common/errors/captcha-error';
51
-
52
- import MeetingCollection from './collection';
53
- import MeetingsUtil from './util';
54
-
55
- /**
56
- * Meetings Ready Event
57
- * Emitted when the meetings instance on webex is ready
58
- * @event meetings:ready
59
- * @instance
60
- * @memberof Meetings
61
- */
62
-
63
- /**
64
- * Meetings Network Disconnected Event
65
- * Emitted when the meetings instance is disconnected from
66
- * the internal mercury server
67
- * @event network:disconnected
68
- * @instance
69
- * @memberof Meetings
70
- */
71
-
72
- /**
73
- * Meetings Registered Event
74
- * Emitted when the meetings instance has been registered and listening
75
- * @event meetings:registered
76
- * @instance
77
- * @memberof Meetings
78
- */
79
-
80
- /**
81
- * Meeting Removed Event
82
- * Emitted when a meeting was removed from the cache of meetings
83
- * @event meeting:removed
84
- * @instance
85
- * @type {Object}
86
- * @property {String} meetingId the removed meeting
87
- * @property {Object} response the server response
88
- * @property {String} type what type of meeting it was
89
- * @memberof Meetings
90
- */
91
-
92
- /**
93
- * Meeting Added Event
94
- * Emitted when a meeting was added to the cache of meetings
95
- * @event meeting:added
96
- * @instance
97
- * @type {Object}
98
- * @property {String} meetingId the added meeting
99
- * @property {String} type what type of meeting it was
100
- * @memberof Meetings
101
- */
102
-
103
- /**
104
- * Maintain a cache of meetings and sync with services.
105
- * @class
106
- */
107
- export default class Meetings extends WebexPlugin {
108
- loggerRequest: any;
109
- media: any;
110
- meetingCollection: any;
111
- personalMeetingRoom: any;
112
- preferredWebexSite: any;
113
- reachability: any;
114
- registered: any;
115
- request: any;
116
- geoHintInfo: any;
117
- meetingInfo: any;
118
-
119
- namespace = MEETINGS;
120
-
121
- /**
122
- * Initializes the Meetings Plugin
123
- * @constructor
124
- * @public
125
- * @memberof Meetings
126
- */
127
- constructor(...args) {
128
- super(...args);
129
-
130
- /**
131
- * The Meetings request to interact with server
132
- * @instance
133
- * @type {Object}
134
- * @private
135
- * @memberof Meetings
136
- */
137
- // @ts-ignore
138
- this.request = new Request({}, {parent: this.webex});
139
- /**
140
- * Log upload request helper
141
- * @instance
142
- * @type {Object}
143
- * @private
144
- * @memberof Meetings
145
- */
146
- // @ts-ignore
147
- this.loggerRequest = new LoggerRequest({webex: this.webex});
148
- this.meetingCollection = new MeetingCollection();
149
- /**
150
- * The PersonalMeetingRoom object to interact with server
151
- * @instance
152
- * @type {Object}
153
- * @public
154
- * @memberof Meetings
155
- */
156
- this.personalMeetingRoom = null;
157
- /**
158
- * The Reachability object to interact with server, starts as null until {@link Meeting#setReachability} is called
159
- * starts as null
160
- * @instance
161
- * @type {Object}
162
- * @private
163
- * @memberof Meetings
164
- */
165
- this.reachability = null;
166
-
167
- /**
168
- * If the meetings plugin has been registered and listening via {@link Meetings#register}
169
- * @instance
170
- * @type {Boolean}
171
- * @public
172
- * @memberof Meetings
173
- */
174
- this.registered = false;
175
-
176
- /**
177
- * This values indicates the preferred webex site the user will start there meeting, getsits value from {@link Meetings#register}
178
- * @instance
179
- * @type {String}
180
- * @private
181
- * @memberof Meetings
182
- */
183
- this.preferredWebexSite = '';
184
-
185
- /**
186
- * The public interface for the internal Media util files. These are helpful to expose outside the context
187
- * of a meeting so that a user can access media without creating a meeting instance.
188
- * @instance
189
- * @type {Object}
190
- * @private
191
- * @memberof Meetings
192
- */
193
- this.media = {
194
- getUserMedia: Media.getUserMedia,
195
- getSupportedDevice: Media.getSupportedDevice,
196
- };
197
-
198
- this.onReady();
199
- }
200
-
201
- /**
202
- * handle locus events and takes meeting actions with them as they come in
203
- * @param {Object} data a locus event
204
- * @param {String} data.locusUrl
205
- * @param {Object} data.locus
206
- * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
207
- * @param {String} data.eventType
208
- * @returns {undefined}
209
- * @private
210
- * @memberof Meetings
211
- */
212
- private handleLocusEvent(data: {locusUrl: string; locus: any}, useRandomDelayForInfo = false) {
213
- let meeting = null;
214
-
215
- // getting meeting by correlationId. This will happen for the new event
216
- // Either the locus
217
- // TODO : Add check for the callBack Address
218
- meeting =
219
- this.meetingCollection.getByKey(LOCUS_URL, data.locusUrl) ||
220
- // @ts-ignore
221
- this.meetingCollection.getByKey(
222
- CORRELATION_ID,
223
- // @ts-ignore
224
- MeetingsUtil.checkForCorrelationId(this.webex.internal.device.url, data.locus)
225
- ) ||
226
- this.meetingCollection.getByKey(
227
- SIP_URI,
228
- data.locus.self &&
229
- data.locus.self.callbackInfo &&
230
- data.locus.self.callbackInfo.callbackAddress
231
- ) ||
232
- (data.locus.info?.isUnifiedSpaceMeeting
233
- ? undefined
234
- : this.meetingCollection.getByKey(CONVERSATION_URL, data.locus.conversationUrl));
235
-
236
- // Special case when locus has got replaced, This only happend once if a replace locus exists
237
- // https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-changing-mid-call
238
-
239
- if (!meeting && data.locus?.replaces?.length > 0) {
240
- // Always the last element in the replace is the active one
241
- meeting = this.meetingCollection.getByKey(
242
- LOCUS_URL,
243
- data.locus.replaces[data.locus.replaces.length - 1].locusUrl
244
- );
245
- }
246
-
247
- if (!meeting) {
248
- // TODO: create meeting when we get a meeting object
249
- // const checkForEnded = (locus) => {
250
- // TODO: you already ended the meeting but you got an event later
251
- // Mainly for 1:1 Callsor meeting
252
- // Happens mainly after refresh
253
-
254
- // 1:1 Meeting
255
- // 1) You ended a call before but you got a mercury event
256
- // Make sure end the call and cleanup the meeting only if the mercury
257
- // event says so
258
- // 2) Maintain lastSync time in the meetings object which helps to compare
259
- // If the meeting came befor or after the sync . ANy meeting start time before the sync time is invalid
260
-
261
- // For space Meeting
262
- // Check the locus object and see who has joined
263
-
264
- // };
265
- // rather then locus object change to locus url
266
-
267
- if (
268
- data.locus &&
269
- data.locus.fullState &&
270
- data.locus.fullState.state === LOCUS.STATE.INACTIVE
271
- ) {
272
- // just ignore the event as its already ended and not active
273
- LoggerProxy.logger.warn(
274
- 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
275
- );
276
-
277
- return;
278
- }
279
-
280
- // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
281
- // Any future events will be neglected
282
-
283
- if (
284
- data.locus &&
285
- data.locus.self &&
286
- data.locus.self.state === _LEFT_ &&
287
- data.locus.self.removed === true
288
- ) {
289
- // just ignore the event as its already ended and not active
290
- LoggerProxy.logger.warn(
291
- 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
292
- );
293
-
294
- return;
295
- }
296
-
297
- this.create(data.locus, _LOCUS_ID_, useRandomDelayForInfo)
298
- .then((newMeeting) => {
299
- meeting = newMeeting;
300
-
301
- // It's a new meeting so initialize the locus data
302
- meeting.locusInfo.initialSetup(data.locus);
303
- })
304
- .catch((e) => {
305
- LoggerProxy.logger.error(e);
306
- })
307
- .finally(() => {
308
- // There will be cases where locus event comes in gets created and deleted because its a 1:1 and meeting gets deleted
309
- // because the other user left so before sending 'added' event make sure it exists in the collection
310
-
311
- if (this.getMeetingByType(_ID_, meeting.id)) {
312
- Metrics.postEvent({
313
- event: eventType.REMOTE_STARTED,
314
- meeting,
315
- data: {trigger: trigger.MERCURY_EVENT},
316
- });
317
- Trigger.trigger(
318
- this,
319
- {
320
- file: 'meetings',
321
- function: 'handleLocusEvent',
322
- },
323
- EVENT_TRIGGERS.MEETING_ADDED,
324
- {
325
- meeting,
326
- type: meeting.type === _MEETING_ ? _JOIN_ : _INCOMING_,
327
- }
328
- );
329
- } else {
330
- // Meeting got added but was not found in the collection. It might have got destroyed
331
- LoggerProxy.logger.warn(
332
- 'Meetings:index#handleLocusEvent --> Created and destroyed meeting object before sending an event'
333
- );
334
- }
335
- });
336
- } else {
337
- meeting.locusInfo.parse(meeting, data);
338
- }
339
- }
340
-
341
- /**
342
- * handles locus events through mercury that are not roap
343
- * @param {Object} envelope
344
- * @param {Object} envelope.data
345
- * @param {String} envelope.data.eventType
346
- * @returns {undefined}
347
- * @private
348
- * @memberof Meetings
349
- */
350
- private handleLocusMercury(envelope: {data: any}) {
351
- const {data} = envelope;
352
- // eslint-disable-next-line @typescript-eslint/no-shadow
353
- const {eventType} = data;
354
-
355
- if (eventType && eventType !== LOCUSEVENT.MESSAGE_ROAP) {
356
- this.handleLocusEvent(data, true);
357
- }
358
- }
359
-
360
- /**
361
- * handles mecury offline event
362
- * @returns {undefined}
363
- * @private
364
- * @memberof Meetings
365
- */
366
- private handleMercuryOffline() {
367
- Trigger.trigger(
368
- this,
369
- {
370
- file: 'meetings/index',
371
- function: 'handleMercuryOffline',
372
- },
373
- EVENT_TRIGGERS.MEETINGS_NETWORK_DISCONNECTED
374
- );
375
- }
376
-
377
- /**
378
- * registers for locus and roap mercury events
379
- * @returns {undefined}
380
- * @private
381
- * @memberof Meetings
382
- */
383
- private listenForEvents() {
384
- // @ts-ignore
385
- this.webex.internal.mercury.on(LOCUSEVENT.LOCUS_MERCURY, (envelope) => {
386
- this.handleLocusMercury(envelope);
387
- });
388
- // @ts-ignore
389
- this.webex.internal.mercury.on(ROAP.ROAP_MERCURY, (envelope) => {
390
- MeetingsUtil.handleRoapMercury(envelope, this.meetingCollection);
391
- });
392
-
393
- // @ts-ignore
394
- this.webex.internal.mercury.on(ONLINE, () => {
395
- this.syncMeetings();
396
- });
397
-
398
- // @ts-ignore
399
- this.webex.internal.mercury.on(OFFLINE, () => {
400
- this.handleMercuryOffline();
401
- });
402
- }
403
-
404
- /**
405
- * stops listening for locus and roap mercury events
406
- * @returns {undefined}
407
- * @private
408
- * @memberof Meetings
409
- */
410
- private stopListeningForEvents() {
411
- // @ts-ignore
412
- this.webex.internal.mercury.off(LOCUSEVENT.LOCUS_MERCURY);
413
- // @ts-ignore
414
- this.webex.internal.mercury.off(ROAP.ROAP_MERCURY);
415
- // @ts-ignore
416
- this.webex.internal.mercury.off(ONLINE);
417
- }
418
-
419
- /**
420
- * @returns {undefined}
421
- * @private
422
- * @memberof Meetings
423
- */
424
- private onReady() {
425
- // @ts-ignore
426
- this.webex.once(READY, () => {
427
- // @ts-ignore
428
- StaticConfig.set(this.config);
429
- // @ts-ignore
430
- LoggerConfig.set(this.config.logging);
431
- // @ts-ignore
432
- LoggerProxy.set(this.webex.logger);
433
-
434
- /**
435
- * The MeetingInfo object to interact with server
436
- * @instance
437
- * @type {Object}
438
- * @private
439
- * @memberof Meetings
440
- */
441
- // @ts-ignore
442
- this.meetingInfo = this.config.experimental.enableUnifiedMeetings
443
- ? // @ts-ignore
444
- new MeetingInfoV2(this.webex)
445
- : // @ts-ignore
446
- new MeetingInfo(this.webex);
447
- // @ts-ignore
448
- this.personalMeetingRoom = new PersonalMeetingRoom(
449
- {meetingInfo: this.meetingInfo},
450
- // @ts-ignore
451
- {parent: this.webex}
452
- );
453
-
454
- Trigger.trigger(
455
- this,
456
- {
457
- file: 'meetings',
458
- function: 'onReady',
459
- },
460
- EVENT_TRIGGERS.MEETINGS_READY
461
- );
462
-
463
- MeetingsUtil.checkH264Support({disableNotifications: true});
464
- // @ts-ignore
465
- Metrics.initialSetup(this.meetingCollection, this.webex);
466
- });
467
- }
468
-
469
- /**
470
- * API to toggle unified meetings
471
- * @param {Boolean} changeState
472
- * @private
473
- * @memberof Meetings
474
- * @returns {undefined}
475
- */
476
- private _toggleUnifiedMeetings(changeState: boolean) {
477
- if (typeof changeState !== 'boolean') {
478
- return;
479
- }
480
- // @ts-ignore
481
- if (this.config?.experimental?.enableUnifiedMeetings !== changeState) {
482
- // @ts-ignore
483
- this.config.experimental.enableUnifiedMeetings = changeState;
484
- // @ts-ignore
485
- this.meetingInfo = changeState ? new MeetingInfoV2(this.webex) : new MeetingInfo(this.webex);
486
- }
487
- }
488
-
489
- /**
490
- * API to enable or disable TURN discovery
491
- * @param {Boolean} enable
492
- * @private
493
- * @memberof Meetings
494
- * @returns {undefined}
495
- */
496
- private _toggleTurnDiscovery(enable: boolean) {
497
- if (typeof enable !== 'boolean') {
498
- return;
499
- }
500
- // @ts-ignore
501
- this.config.experimental.enableTurnDiscovery = enable;
502
- }
503
-
504
- /**
505
- * API to toggle starting adhoc meeting
506
- * @param {Boolean} changeState
507
- * @private
508
- * @memberof Meetings
509
- * @returns {undefined}
510
- */
511
- private _toggleAdhocMeetings(changeState: boolean) {
512
- if (typeof changeState !== 'boolean') {
513
- return;
514
- }
515
- // @ts-ignore
516
- if (this.config?.experimental?.enableAdhocMeetings !== changeState) {
517
- // @ts-ignore
518
- this.config.experimental.enableAdhocMeetings = changeState;
519
- }
520
- }
521
-
522
- /**
523
- * Explicitly sets up the meetings plugin by registering
524
- * the device, connecting to mercury, and listening for locus events.
525
- *
526
- * @returns {Promise}
527
- * @public
528
- * @memberof Meetings
529
- */
530
- public register() {
531
- // @ts-ignore
532
- if (!this.webex.canAuthorize) {
533
- LoggerProxy.logger.error(
534
- 'Meetings:index#register --> ERROR, Unable to register, SDK cannot authorize'
535
- );
536
-
537
- return Promise.reject(new Error('SDK cannot authorize'));
538
- }
539
-
540
- if (this.registered) {
541
- LoggerProxy.logger.info(
542
- 'Meetings:index#register --> INFO, Meetings plugin already registered'
543
- );
544
-
545
- return Promise.resolve();
546
- }
547
-
548
- return Promise.all([
549
- this.fetchUserPreferredWebexSite(),
550
- this.getGeoHint(),
551
- this.startReachability().catch((error) => {
552
- LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
553
- }),
554
- // @ts-ignore
555
- this.webex.internal.device
556
- .register()
557
- // @ts-ignore
558
- .then(() =>
559
- LoggerProxy.logger.info(
560
- // @ts-ignore
561
- `Meetings:index#register --> INFO, Device registered ${this.webex.internal.device.url}`
562
- )
563
- )
564
- // @ts-ignore
565
- .then(() => this.webex.internal.mercury.connect()),
566
- MeetingsUtil.checkH264Support.call(this),
567
- ])
568
- .then(() => {
569
- this.listenForEvents();
570
- Trigger.trigger(
571
- this,
572
- {
573
- file: 'meetings',
574
- function: 'register',
575
- },
576
- EVENT_TRIGGERS.MEETINGS_REGISTERED
577
- );
578
- this.registered = true;
579
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_SUCCESS);
580
- })
581
- .catch((error) => {
582
- LoggerProxy.logger.error(
583
- `Meetings:index#register --> ERROR, Unable to register, ${error.message}`
584
- );
585
-
586
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_FAILED, {
587
- reason: error.message,
588
- stack: error.stack,
589
- });
590
-
591
- return Promise.reject(error);
592
- });
593
- }
594
-
595
- /**
596
- * Explicitly tears down the meetings plugin by deregistering
597
- * the device, disconnecting from mercury, and stops listening to locus events
598
- *
599
- * @returns {Promise}
600
- * @public
601
- * @memberof Meetings
602
- */
603
- unregister() {
604
- if (!this.registered) {
605
- LoggerProxy.logger.info(
606
- 'Meetings:index#unregister --> INFO, Meetings plugin already unregistered'
607
- );
608
-
609
- return Promise.resolve();
610
- }
611
-
612
- this.stopListeningForEvents();
613
-
614
- return (
615
- // @ts-ignore
616
- this.webex.internal.mercury
617
- .disconnect()
618
- // @ts-ignore
619
- .then(() => this.webex.internal.device.unregister())
620
- .then(() => {
621
- Trigger.trigger(
622
- this,
623
- {
624
- file: 'meetings',
625
- function: 'unregister',
626
- },
627
- EVENT_TRIGGERS.MEETINGS_UNREGISTERED
628
- );
629
- this.registered = false;
630
- })
631
- );
632
- }
633
-
634
- /**
635
- * Uploads logs to the webex services for tracking
636
- * @param {Object} [options={}]
637
- * @param {String} [options.callStart] Call Start Time
638
- * @param {String} [options.feedbackId] ID used for tracking
639
- * @param {String} [options.locusId]
640
- * @param {String} [options.correlationId]
641
- * @param {String} [options.meetingId] webex meeting ID
642
- * @param {String} [options.userId] userId
643
- * @param {String} [options.orgId] org id
644
- * @returns {String} feedback ID logs were submitted under
645
- */
646
- uploadLogs(
647
- options: {
648
- callStart?: string;
649
- feedbackId?: string;
650
- locusId?: string;
651
- correlationId?: string;
652
- meetingId?: string;
653
- userId?: string;
654
- orgId?: string;
655
- } = {}
656
- ) {
657
- LoggerProxy.logger.info('Meetings:index#uploadLogs --> uploading logs');
658
-
659
- return this.loggerRequest
660
- .uploadLogs(options)
661
- .then((uploadResult) => {
662
- LoggerProxy.logger.info(
663
- 'Meetings:index#uploadLogs --> Upload logs for meeting completed.',
664
- uploadResult
665
- );
666
- Trigger.trigger(
667
- this,
668
- {
669
- file: 'meetings',
670
- function: 'uploadLogs',
671
- },
672
- EVENT_TRIGGERS.MEETING_LOG_UPLOAD_SUCCESS,
673
- {
674
- meetingId: options.meetingId,
675
- details: uploadResult,
676
- }
677
- );
678
-
679
- return uploadResult;
680
- })
681
- .catch((uploadError) => {
682
- LoggerProxy.logger.error(
683
- 'Meetings:index#uploadLogs --> Unable to upload logs for meeting',
684
- uploadError
685
- );
686
- Trigger.trigger(
687
- this,
688
- {
689
- file: 'meetings',
690
- function: 'uploadLogs',
691
- },
692
- EVENT_TRIGGERS.MEETING_LOG_UPLOAD_FAILURE,
693
- {
694
- meetingId: options.meetingId,
695
- reason: uploadError,
696
- }
697
- );
698
-
699
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.UPLOAD_LOGS_FAILURE, {
700
- // @ts-ignore - seems like typo
701
- meetingId: options.meetingsId,
702
- reason: uploadError.message,
703
- stack: uploadError.stack,
704
- code: uploadError.code,
705
- });
706
- });
707
- }
708
-
709
- /**
710
- * initializes the reachability instance for Meetings
711
- * @returns {undefined}
712
- * @public
713
- * @memberof Meetings
714
- */
715
- setReachability() {
716
- // @ts-ignore
717
- this.reachability = new Reachability(this.webex);
718
- }
719
-
720
- /**
721
- * gets the reachability instance for Meetings
722
- * @returns {Reachability}
723
- * @public
724
- * @memberof Meetings
725
- */
726
- getReachability() {
727
- return this.reachability;
728
- }
729
-
730
- /**
731
- * initializes and starts gathering reachability for Meetings
732
- * @returns {Promise}
733
- * @public
734
- * @memberof Meetings
735
- */
736
- startReachability() {
737
- if (!this.reachability) {
738
- this.setReachability();
739
- }
740
-
741
- return this.getReachability().gatherReachability();
742
- }
743
-
744
- /**
745
- * Get geoHint for info for meetings
746
- * @returns {Promise}
747
- * @private
748
- * @memberof Meetings
749
- */
750
- getGeoHint() {
751
- return this.request.fetchGeoHint().then((res) => {
752
- this.geoHintInfo = res;
753
- });
754
- }
755
-
756
- /**
757
- * Fetch user preferred webex site information
758
- * This also has other infomation about the user
759
- * @returns {Promise}
760
- * @private
761
- * @memberof Meetings
762
- */
763
- fetchUserPreferredWebexSite() {
764
- return this.request.getMeetingPreferences().then((res) => {
765
- if (res) {
766
- this.preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
767
- }
768
- });
769
- }
770
-
771
- /**
772
- * gets the personal meeting room instance, for saved PMR values for this user
773
- * @returns {PersonalMeetingRoom}
774
- * @public
775
- * @memberof Meetings
776
- */
777
-
778
- getPersonalMeetingRoom() {
779
- return this.personalMeetingRoom;
780
- }
781
-
782
- /**
783
- * @param {Meeting} meeting
784
- * @param {Object} reason
785
- * @param {String} type
786
- * @returns {Undefined}
787
- * @private
788
- * @memberof Meetings
789
- */
790
- private destroy(meeting: Meeting, reason: object) {
791
- MeetingUtil.cleanUp(meeting);
792
- this.meetingCollection.delete(meeting.id);
793
- Trigger.trigger(
794
- this,
795
- {
796
- file: 'meetings',
797
- function: 'destroy',
798
- },
799
- EVENT_TRIGGERS.MEETING_REMOVED,
800
- {
801
- meetingId: meeting.id,
802
- reason,
803
- }
804
- );
805
- }
806
-
807
- /**
808
- * Create a meeting.
809
- * @param {string} destination - sipURL, spaceId, phonenumber, or locus object}
810
- * @param {string} [type] - the optional specified type, such as locusId
811
- * @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
812
- * @returns {Promise<Meeting>} A new Meeting.
813
- * @public
814
- * @memberof Meetings
815
- */
816
- public create(destination: string, type: string = null, useRandomDelayForInfo = false) {
817
- // TODO: type should be from a dictionary
818
-
819
- // Validate meeting information based on the provided destination and
820
- // type. This must be performed prior to determining if the meeting is
821
- // found in the collection, as we mutate the destination for hydra person
822
- // id values.
823
- return (
824
- this.meetingInfo
825
- .fetchInfoOptions(destination, type)
826
- // Catch a failure to fetch info options.
827
- .catch((error) => {
828
- LoggerProxy.logger.info(
829
- `Meetings:index#create --> INFO, unable to determine info options: ${error.message}`
830
- );
831
- })
832
- .then((options: any = {}) => {
833
- // Normalize the destination.
834
- const targetDest = options.destination || destination;
835
-
836
- // check for the conversation URL then sip Url
837
- let meeting = null;
838
-
839
- if (type === _CONVERSATION_URL_ || options.type === _CONVERSATION_URL_) {
840
- const foundMeeting = this.meetingCollection.getByKey(CONVERSATION_URL, targetDest);
841
-
842
- if (foundMeeting) {
843
- const foundMeetingIsNotCalendarMeeting = !foundMeeting.locusInfo.scheduledMeeting;
844
-
845
- // If the found meeting is not a calendar meeting, return that meeting.
846
- // This allows for the creation of instant-meetings when calendar meetings are present.
847
- if (foundMeetingIsNotCalendarMeeting) {
848
- meeting = foundMeeting;
849
- }
850
- }
851
- }
852
-
853
- // Attempt to collect the meeting if it exists.
854
- if (!meeting) {
855
- meeting = this.meetingCollection.getByKey(SIP_URI, targetDest);
856
- }
857
-
858
- // Validate if a meeting was found.
859
- if (!meeting) {
860
- // Create a meeting based on the normalized destination and type.
861
- return this.createMeeting(targetDest, type, useRandomDelayForInfo).then(
862
- (createdMeeting: any) => {
863
- // If the meeting was successfully created.
864
- if (createdMeeting && createdMeeting.on) {
865
- // Create a destruction event for the meeting.
866
- createdMeeting.on(EVENTS.DESTROY_MEETING, (payload) => {
867
- // @ts-ignore
868
- if (this.config.autoUploadLogs) {
869
- this.uploadLogs({
870
- callStart: createdMeeting.locusInfo?.fullState?.lastActive,
871
- correlationId: createdMeeting.correlationId,
872
- feedbackId: createdMeeting.correlationId,
873
- locusId: createdMeeting.locusId,
874
- meetingId: createdMeeting.locusInfo?.info?.webExMeetingId,
875
- }).then(() => this.destroy(createdMeeting, payload.reason));
876
- } else {
877
- this.destroy(createdMeeting, payload.reason);
878
- }
879
- });
880
-
881
- createdMeeting.on(EVENTS.REQUEST_UPLOAD_LOGS, (meetingInstance) => {
882
- // @ts-ignore
883
- if (this.config.autoUploadLogs) {
884
- this.uploadLogs({
885
- callStart: meetingInstance?.locusInfo?.fullState?.lastActive,
886
- correlationId: meetingInstance.correlationId,
887
- feedbackId: meetingInstance.correlationId,
888
- locusId: meetingInstance.locusId,
889
- meetingId: meetingInstance.locusInfo?.info?.webExMeetingId,
890
- });
891
- }
892
- });
893
- } else {
894
- LoggerProxy.logger.error(
895
- `Meetings:index#create --> ERROR, meeting does not have on method, will not be destroyed, meeting cleanup impossible for meeting: ${meeting}`
896
- );
897
- }
898
-
899
- // Return the newly created meeting.
900
- return Promise.resolve(createdMeeting);
901
- }
902
- );
903
- }
904
-
905
- // Return the existing meeting.
906
- return Promise.resolve(meeting);
907
- })
908
- );
909
- }
910
-
911
- /**
912
- * @param {String} destination see create()
913
- * @param {String} type see create()
914
- * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
915
- * @returns {Promise} a new meeting instance complete with meeting info and destination
916
- * @private
917
- * @memberof Meetings
918
- */
919
- private async createMeeting(
920
- destination: any,
921
- type: string = null,
922
- useRandomDelayForInfo = false
923
- ) {
924
- const meeting = new Meeting(
925
- {
926
- // @ts-ignore
927
- userId: this.webex.internal.device.userId,
928
- // @ts-ignore
929
- deviceUrl: this.webex.internal.device.url,
930
- // @ts-ignore
931
- orgId: this.webex.internal.device.orgId,
932
- roapSeq: 0,
933
- locus: type === _LOCUS_ID_ ? destination : null, // pass the locus object if present
934
- meetingInfoProvider: this.meetingInfo,
935
- destination,
936
- destinationType: type,
937
- },
938
- {
939
- // @ts-ignore
940
- parent: this.webex,
941
- }
942
- );
943
-
944
- this.meetingCollection.set(meeting);
945
-
946
- try {
947
- // if no participant has joined the scheduled meeting (meaning meeting is not active) and we get a locusEvent,
948
- // it means the meeting will start in 5-6 min. In that case, we want to fetchMeetingInfo
949
- // between 5 and 2 min (random between 3 minutes) before the meeting starts
950
- // to avoid a spike in traffic to the wbxappi service
951
- let waitingTime = 0;
952
-
953
- if (destination.meeting) {
954
- const {startTime} = destination.meeting;
955
- const startTimeDate = new Date(startTime);
956
- const startTimeDatestamp = startTimeDate.getTime();
957
- const timeToStart = startTimeDatestamp - Date.now();
958
- const maxWaitingTime = Math.max(
959
- Math.min(timeToStart, MAX_RANDOM_DELAY_FOR_MEETING_INFO),
960
- 0
961
- );
962
-
963
- waitingTime = Math.round(Math.random() * maxWaitingTime);
964
- }
965
- const isMeetingActive = !!destination.fullState?.active;
966
- // @ts-ignore
967
- const {enableUnifiedMeetings} = this.config.experimental;
968
-
969
- if (enableUnifiedMeetings && !isMeetingActive && useRandomDelayForInfo && waitingTime > 0) {
970
- meeting.fetchMeetingInfoTimeoutId = setTimeout(
971
- () => meeting.fetchMeetingInfo({}),
972
- waitingTime
973
- );
974
- meeting.parseMeetingInfo(undefined, destination);
975
- } else {
976
- await meeting.fetchMeetingInfo({});
977
- }
978
- } catch (err) {
979
- if (!(err instanceof CaptchaError) && !(err instanceof PasswordError)) {
980
- // if there is no meeting info we assume its a 1:1 call or wireless share
981
- LoggerProxy.logger.info(
982
- `Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
983
- );
984
- LoggerProxy.logger.info(
985
- 'Meetings:index#createMeeting --> Info assuming this destination is a 1:1 or wireless share'
986
- );
987
- }
988
- LoggerProxy.logger.debug(
989
- `Meetings:index#createMeeting --> Debug ${err} fetching /meetingInfo for creation.`
990
- );
991
- } finally {
992
- // For type LOCUS_ID we need to parse the locus object to get the information
993
- // about the caller and callee
994
- // Meeting Added event will be created in `handleLocusEvent`
995
- if (type !== _LOCUS_ID_) {
996
- if (!meeting.sipUri) {
997
- meeting.setSipUri(destination);
998
- }
999
-
1000
- // TODO: check if we have to move this to parser
1001
- const meetingAddedType = MeetingsUtil.getMeetingAddedType(type);
1002
-
1003
- // We typically shouldn't need to trigger both and event and return a promise.
1004
- // Is this a special case? We want to make the public API usage as simple as possible.
1005
- Trigger.trigger(
1006
- this,
1007
- {
1008
- file: 'meetings',
1009
- function: 'createMeeting',
1010
- },
1011
- EVENT_TRIGGERS.MEETING_ADDED,
1012
- {
1013
- meeting,
1014
- type: meetingAddedType,
1015
- }
1016
- );
1017
- }
1018
- }
1019
-
1020
- return meeting;
1021
-
1022
- // Create the meeting calling the necessary service endpoints.
1023
-
1024
- // Internally, there are many more destinations:
1025
- //
1026
- // - locusID
1027
- // - meetingURL
1028
- // - globalMeetingID, e.g, *00*meetingID
1029
- // - meetingID
1030
- // - meetingURL
1031
- // - PSTN
1032
- // - phone number
1033
- //
1034
- // Our job is to determine the appropriate one
1035
- // and its corresponding service so that developers
1036
- // need only sipURL or spaceID to get a meeting
1037
- // and its ID, but have the option to use createWithType()
1038
- // and specify those types to get meetingInfo
1039
- }
1040
-
1041
- /**
1042
- * get a specifc meeting given it's type matched to the value, i.e., locus url
1043
- * @param {String} type
1044
- * @param {Object} value
1045
- * @returns {Meeting}
1046
- * @public
1047
- * @memberof Meetings
1048
- */
1049
- public getMeetingByType(type: string, value: object) {
1050
- return this.meetingCollection.getByKey(type, value);
1051
- }
1052
-
1053
- /**
1054
- * Get all meetings.
1055
- * @param {object} options
1056
- * @param {object} options.startDate - get meetings after this start date
1057
- * @param {object} options.endDate - get meetings before this end date
1058
- * @returns {Object} All currently active meetings.
1059
- * @public
1060
- * @memberof Meetings
1061
- */
1062
- public getAllMeetings(
1063
- options: {
1064
- startDate: object;
1065
- endDate: object;
1066
- } = {} as any
1067
- ) {
1068
- // Options may include other parameters to filter this collection
1069
- // of meetings.
1070
- return this.meetingCollection.getAll(options);
1071
- }
1072
-
1073
- /**
1074
- * syncs all the meeting from server
1075
- * @returns {undefined}
1076
- * @public
1077
- * @memberof Meetings
1078
- */
1079
- public syncMeetings() {
1080
- return this.request.getActiveMeetings().then((locusArray) => {
1081
- const activeLocusUrl = [];
1082
-
1083
- if (locusArray?.loci && locusArray.loci.length > 0) {
1084
- locusArray.loci.forEach((locus) => {
1085
- activeLocusUrl.push(locus.url);
1086
- this.handleLocusEvent({
1087
- locus,
1088
- locusUrl: locus.url,
1089
- });
1090
- });
1091
- }
1092
- const meetingsCollection = this.meetingCollection.getAll();
1093
-
1094
- if (Object.keys(meetingsCollection).length > 0) {
1095
- // Some time the mercury event is missed after mercury reconnect
1096
- // if sync returns no locus then clear all the meetings
1097
- for (const meeting of Object.values(meetingsCollection)) {
1098
- // @ts-ignore
1099
- if (!activeLocusUrl.includes(meeting.locusUrl)) {
1100
- // destroy function also uploads logs
1101
- // @ts-ignore
1102
- this.destroy(meeting, MEETING_REMOVED_REASON.NO_MEETINGS_TO_SYNC);
1103
- }
1104
- }
1105
- }
1106
- });
1107
- }
1108
-
1109
- /**
1110
- * Get all scheduled meetings.
1111
- * @param {object} options
1112
- * @param {object} options.startDate - get meetings after this start date
1113
- * @param {object} options.endDate - get meetings before this end date
1114
- * @returns {Object} All scheduled meetings.
1115
- * @memberof Meetings
1116
- */
1117
- getScheduledMeetings() {
1118
- return this.meetingCollection.getAll({scheduled: true});
1119
- }
1120
-
1121
- /**
1122
- * Get the logger instance for plugin-meetings
1123
- * @returns {Logger}
1124
- */
1125
- getLogger() {
1126
- return LoggerProxy.get();
1127
- }
1128
- }
1
+ /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
2
+
3
+ import '@webex/internal-plugin-mercury';
4
+ // @ts-ignore
5
+ import {WebexPlugin} from '@webex/webex-core';
6
+
7
+ import 'webrtc-adapter';
8
+
9
+ import Metrics from '../metrics';
10
+ import {trigger, eventType} from '../metrics/config';
11
+ import LoggerConfig from '../common/logs/logger-config';
12
+ import StaticConfig from '../common/config';
13
+ import LoggerProxy from '../common/logs/logger-proxy';
14
+ import LoggerRequest from '../common/logs/request';
15
+ import Trigger from '../common/events/trigger-proxy';
16
+ import Media from '../media';
17
+ import MeetingUtil from '../meeting/util';
18
+ import {
19
+ MEETINGS,
20
+ EVENTS,
21
+ EVENT_TRIGGERS,
22
+ READY,
23
+ LOCUSEVENT,
24
+ LOCUS_URL,
25
+ MAX_RANDOM_DELAY_FOR_MEETING_INFO,
26
+ ROAP,
27
+ ONLINE,
28
+ OFFLINE,
29
+ _MEETING_,
30
+ _JOIN_,
31
+ _LOCUS_ID_,
32
+ _INCOMING_,
33
+ LOCUS,
34
+ CORRELATION_ID,
35
+ SIP_URI,
36
+ _LEFT_,
37
+ _ID_,
38
+ MEETING_REMOVED_REASON,
39
+ _CONVERSATION_URL_,
40
+ CONVERSATION_URL,
41
+ } from '../constants';
42
+ import BEHAVIORAL_METRICS from '../metrics/constants';
43
+ import MeetingInfo from '../meeting-info';
44
+ import MeetingInfoV2 from '../meeting-info/meeting-info-v2';
45
+ import Meeting from '../meeting';
46
+ import PersonalMeetingRoom from '../personal-meeting-room';
47
+ import Reachability from '../reachability';
48
+ import Request from './request';
49
+ import PasswordError from '../common/errors/password-error';
50
+ import CaptchaError from '../common/errors/captcha-error';
51
+
52
+ import MeetingCollection from './collection';
53
+ import MeetingsUtil from './util';
54
+
55
+ /**
56
+ * Meetings Ready Event
57
+ * Emitted when the meetings instance on webex is ready
58
+ * @event meetings:ready
59
+ * @instance
60
+ * @memberof Meetings
61
+ */
62
+
63
+ /**
64
+ * Meetings Network Disconnected Event
65
+ * Emitted when the meetings instance is disconnected from
66
+ * the internal mercury server
67
+ * @event network:disconnected
68
+ * @instance
69
+ * @memberof Meetings
70
+ */
71
+
72
+ /**
73
+ * Meetings Registered Event
74
+ * Emitted when the meetings instance has been registered and listening
75
+ * @event meetings:registered
76
+ * @instance
77
+ * @memberof Meetings
78
+ */
79
+
80
+ /**
81
+ * Meeting Removed Event
82
+ * Emitted when a meeting was removed from the cache of meetings
83
+ * @event meeting:removed
84
+ * @instance
85
+ * @type {Object}
86
+ * @property {String} meetingId the removed meeting
87
+ * @property {Object} response the server response
88
+ * @property {String} type what type of meeting it was
89
+ * @memberof Meetings
90
+ */
91
+
92
+ /**
93
+ * Meeting Added Event
94
+ * Emitted when a meeting was added to the cache of meetings
95
+ * @event meeting:added
96
+ * @instance
97
+ * @type {Object}
98
+ * @property {String} meetingId the added meeting
99
+ * @property {String} type what type of meeting it was
100
+ * @memberof Meetings
101
+ */
102
+
103
+ /**
104
+ * Maintain a cache of meetings and sync with services.
105
+ * @class
106
+ */
107
+ export default class Meetings extends WebexPlugin {
108
+ loggerRequest: any;
109
+ media: any;
110
+ meetingCollection: any;
111
+ personalMeetingRoom: any;
112
+ preferredWebexSite: any;
113
+ reachability: any;
114
+ registered: any;
115
+ request: any;
116
+ geoHintInfo: any;
117
+ meetingInfo: any;
118
+
119
+ namespace = MEETINGS;
120
+
121
+ /**
122
+ * Initializes the Meetings Plugin
123
+ * @constructor
124
+ * @public
125
+ * @memberof Meetings
126
+ */
127
+ constructor(...args) {
128
+ super(...args);
129
+
130
+ /**
131
+ * The Meetings request to interact with server
132
+ * @instance
133
+ * @type {Object}
134
+ * @private
135
+ * @memberof Meetings
136
+ */
137
+ // @ts-ignore
138
+ this.request = new Request({}, {parent: this.webex});
139
+ /**
140
+ * Log upload request helper
141
+ * @instance
142
+ * @type {Object}
143
+ * @private
144
+ * @memberof Meetings
145
+ */
146
+ // @ts-ignore
147
+ this.loggerRequest = new LoggerRequest({webex: this.webex});
148
+ this.meetingCollection = new MeetingCollection();
149
+ /**
150
+ * The PersonalMeetingRoom object to interact with server
151
+ * @instance
152
+ * @type {Object}
153
+ * @public
154
+ * @memberof Meetings
155
+ */
156
+ this.personalMeetingRoom = null;
157
+ /**
158
+ * The Reachability object to interact with server, starts as null until {@link Meeting#setReachability} is called
159
+ * starts as null
160
+ * @instance
161
+ * @type {Object}
162
+ * @private
163
+ * @memberof Meetings
164
+ */
165
+ this.reachability = null;
166
+
167
+ /**
168
+ * If the meetings plugin has been registered and listening via {@link Meetings#register}
169
+ * @instance
170
+ * @type {Boolean}
171
+ * @public
172
+ * @memberof Meetings
173
+ */
174
+ this.registered = false;
175
+
176
+ /**
177
+ * This values indicates the preferred webex site the user will start there meeting, getsits value from {@link Meetings#register}
178
+ * @instance
179
+ * @type {String}
180
+ * @private
181
+ * @memberof Meetings
182
+ */
183
+ this.preferredWebexSite = '';
184
+
185
+ /**
186
+ * The public interface for the internal Media util files. These are helpful to expose outside the context
187
+ * of a meeting so that a user can access media without creating a meeting instance.
188
+ * @instance
189
+ * @type {Object}
190
+ * @private
191
+ * @memberof Meetings
192
+ */
193
+ this.media = {
194
+ getUserMedia: Media.getUserMedia,
195
+ getSupportedDevice: Media.getSupportedDevice,
196
+ };
197
+
198
+ this.onReady();
199
+ }
200
+
201
+ /**
202
+ * handle locus events and takes meeting actions with them as they come in
203
+ * @param {Object} data a locus event
204
+ * @param {String} data.locusUrl
205
+ * @param {Object} data.locus
206
+ * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
207
+ * @param {String} data.eventType
208
+ * @returns {undefined}
209
+ * @private
210
+ * @memberof Meetings
211
+ */
212
+ private handleLocusEvent(data: {locusUrl: string; locus: any}, useRandomDelayForInfo = false) {
213
+ let meeting = null;
214
+
215
+ // getting meeting by correlationId. This will happen for the new event
216
+ // Either the locus
217
+ // TODO : Add check for the callBack Address
218
+ meeting =
219
+ this.meetingCollection.getByKey(LOCUS_URL, data.locusUrl) ||
220
+ // @ts-ignore
221
+ this.meetingCollection.getByKey(
222
+ CORRELATION_ID,
223
+ // @ts-ignore
224
+ MeetingsUtil.checkForCorrelationId(this.webex.internal.device.url, data.locus)
225
+ ) ||
226
+ this.meetingCollection.getByKey(
227
+ SIP_URI,
228
+ data.locus.self &&
229
+ data.locus.self.callbackInfo &&
230
+ data.locus.self.callbackInfo.callbackAddress
231
+ ) ||
232
+ (data.locus.info?.isUnifiedSpaceMeeting
233
+ ? undefined
234
+ : this.meetingCollection.getByKey(CONVERSATION_URL, data.locus.conversationUrl));
235
+
236
+ // Special case when locus has got replaced, This only happend once if a replace locus exists
237
+ // https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-changing-mid-call
238
+
239
+ if (!meeting && data.locus?.replaces?.length > 0) {
240
+ // Always the last element in the replace is the active one
241
+ meeting = this.meetingCollection.getByKey(
242
+ LOCUS_URL,
243
+ data.locus.replaces[data.locus.replaces.length - 1].locusUrl
244
+ );
245
+ }
246
+
247
+ if (!meeting) {
248
+ // TODO: create meeting when we get a meeting object
249
+ // const checkForEnded = (locus) => {
250
+ // TODO: you already ended the meeting but you got an event later
251
+ // Mainly for 1:1 Callsor meeting
252
+ // Happens mainly after refresh
253
+
254
+ // 1:1 Meeting
255
+ // 1) You ended a call before but you got a mercury event
256
+ // Make sure end the call and cleanup the meeting only if the mercury
257
+ // event says so
258
+ // 2) Maintain lastSync time in the meetings object which helps to compare
259
+ // If the meeting came befor or after the sync . ANy meeting start time before the sync time is invalid
260
+
261
+ // For space Meeting
262
+ // Check the locus object and see who has joined
263
+
264
+ // };
265
+ // rather then locus object change to locus url
266
+
267
+ if (
268
+ data.locus &&
269
+ data.locus.fullState &&
270
+ data.locus.fullState.state === LOCUS.STATE.INACTIVE
271
+ ) {
272
+ // just ignore the event as its already ended and not active
273
+ LoggerProxy.logger.warn(
274
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
275
+ );
276
+
277
+ return;
278
+ }
279
+
280
+ // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
281
+ // Any future events will be neglected
282
+
283
+ if (
284
+ data.locus &&
285
+ data.locus.self &&
286
+ data.locus.self.state === _LEFT_ &&
287
+ data.locus.self.removed === true
288
+ ) {
289
+ // just ignore the event as its already ended and not active
290
+ LoggerProxy.logger.warn(
291
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
292
+ );
293
+
294
+ return;
295
+ }
296
+
297
+ this.create(data.locus, _LOCUS_ID_, useRandomDelayForInfo)
298
+ .then((newMeeting) => {
299
+ meeting = newMeeting;
300
+
301
+ // It's a new meeting so initialize the locus data
302
+ meeting.locusInfo.initialSetup(data.locus);
303
+ })
304
+ .catch((e) => {
305
+ LoggerProxy.logger.error(e);
306
+ })
307
+ .finally(() => {
308
+ // There will be cases where locus event comes in gets created and deleted because its a 1:1 and meeting gets deleted
309
+ // because the other user left so before sending 'added' event make sure it exists in the collection
310
+
311
+ if (this.getMeetingByType(_ID_, meeting.id)) {
312
+ Metrics.postEvent({
313
+ event: eventType.REMOTE_STARTED,
314
+ meeting,
315
+ data: {trigger: trigger.MERCURY_EVENT},
316
+ });
317
+ Trigger.trigger(
318
+ this,
319
+ {
320
+ file: 'meetings',
321
+ function: 'handleLocusEvent',
322
+ },
323
+ EVENT_TRIGGERS.MEETING_ADDED,
324
+ {
325
+ meeting,
326
+ type: meeting.type === _MEETING_ ? _JOIN_ : _INCOMING_,
327
+ }
328
+ );
329
+ } else {
330
+ // Meeting got added but was not found in the collection. It might have got destroyed
331
+ LoggerProxy.logger.warn(
332
+ 'Meetings:index#handleLocusEvent --> Created and destroyed meeting object before sending an event'
333
+ );
334
+ }
335
+ });
336
+ } else {
337
+ meeting.locusInfo.parse(meeting, data);
338
+ }
339
+ }
340
+
341
+ /**
342
+ * handles locus events through mercury that are not roap
343
+ * @param {Object} envelope
344
+ * @param {Object} envelope.data
345
+ * @param {String} envelope.data.eventType
346
+ * @returns {undefined}
347
+ * @private
348
+ * @memberof Meetings
349
+ */
350
+ private handleLocusMercury(envelope: {data: any}) {
351
+ const {data} = envelope;
352
+ // eslint-disable-next-line @typescript-eslint/no-shadow
353
+ const {eventType} = data;
354
+
355
+ if (eventType && eventType !== LOCUSEVENT.MESSAGE_ROAP) {
356
+ this.handleLocusEvent(data, true);
357
+ }
358
+ }
359
+
360
+ /**
361
+ * handles mecury offline event
362
+ * @returns {undefined}
363
+ * @private
364
+ * @memberof Meetings
365
+ */
366
+ private handleMercuryOffline() {
367
+ Trigger.trigger(
368
+ this,
369
+ {
370
+ file: 'meetings/index',
371
+ function: 'handleMercuryOffline',
372
+ },
373
+ EVENT_TRIGGERS.MEETINGS_NETWORK_DISCONNECTED
374
+ );
375
+ }
376
+
377
+ /**
378
+ * registers for locus and roap mercury events
379
+ * @returns {undefined}
380
+ * @private
381
+ * @memberof Meetings
382
+ */
383
+ private listenForEvents() {
384
+ // @ts-ignore
385
+ this.webex.internal.mercury.on(LOCUSEVENT.LOCUS_MERCURY, (envelope) => {
386
+ this.handleLocusMercury(envelope);
387
+ });
388
+ // @ts-ignore
389
+ this.webex.internal.mercury.on(ROAP.ROAP_MERCURY, (envelope) => {
390
+ MeetingsUtil.handleRoapMercury(envelope, this.meetingCollection);
391
+ });
392
+
393
+ // @ts-ignore
394
+ this.webex.internal.mercury.on(ONLINE, () => {
395
+ this.syncMeetings();
396
+ });
397
+
398
+ // @ts-ignore
399
+ this.webex.internal.mercury.on(OFFLINE, () => {
400
+ this.handleMercuryOffline();
401
+ });
402
+ }
403
+
404
+ /**
405
+ * stops listening for locus and roap mercury events
406
+ * @returns {undefined}
407
+ * @private
408
+ * @memberof Meetings
409
+ */
410
+ private stopListeningForEvents() {
411
+ // @ts-ignore
412
+ this.webex.internal.mercury.off(LOCUSEVENT.LOCUS_MERCURY);
413
+ // @ts-ignore
414
+ this.webex.internal.mercury.off(ROAP.ROAP_MERCURY);
415
+ // @ts-ignore
416
+ this.webex.internal.mercury.off(ONLINE);
417
+ }
418
+
419
+ /**
420
+ * @returns {undefined}
421
+ * @private
422
+ * @memberof Meetings
423
+ */
424
+ private onReady() {
425
+ // @ts-ignore
426
+ this.webex.once(READY, () => {
427
+ // @ts-ignore
428
+ StaticConfig.set(this.config);
429
+ // @ts-ignore
430
+ LoggerConfig.set(this.config.logging);
431
+ // @ts-ignore
432
+ LoggerProxy.set(this.webex.logger);
433
+
434
+ /**
435
+ * The MeetingInfo object to interact with server
436
+ * @instance
437
+ * @type {Object}
438
+ * @private
439
+ * @memberof Meetings
440
+ */
441
+ // @ts-ignore
442
+ this.meetingInfo = this.config.experimental.enableUnifiedMeetings
443
+ ? // @ts-ignore
444
+ new MeetingInfoV2(this.webex)
445
+ : // @ts-ignore
446
+ new MeetingInfo(this.webex);
447
+ // @ts-ignore
448
+ this.personalMeetingRoom = new PersonalMeetingRoom(
449
+ {meetingInfo: this.meetingInfo},
450
+ // @ts-ignore
451
+ {parent: this.webex}
452
+ );
453
+
454
+ Trigger.trigger(
455
+ this,
456
+ {
457
+ file: 'meetings',
458
+ function: 'onReady',
459
+ },
460
+ EVENT_TRIGGERS.MEETINGS_READY
461
+ );
462
+
463
+ MeetingsUtil.checkH264Support({disableNotifications: true});
464
+ // @ts-ignore
465
+ Metrics.initialSetup(this.meetingCollection, this.webex);
466
+ });
467
+ }
468
+
469
+ /**
470
+ * API to toggle unified meetings
471
+ * @param {Boolean} changeState
472
+ * @private
473
+ * @memberof Meetings
474
+ * @returns {undefined}
475
+ */
476
+ private _toggleUnifiedMeetings(changeState: boolean) {
477
+ if (typeof changeState !== 'boolean') {
478
+ return;
479
+ }
480
+ // @ts-ignore
481
+ if (this.config?.experimental?.enableUnifiedMeetings !== changeState) {
482
+ // @ts-ignore
483
+ this.config.experimental.enableUnifiedMeetings = changeState;
484
+ // @ts-ignore
485
+ this.meetingInfo = changeState ? new MeetingInfoV2(this.webex) : new MeetingInfo(this.webex);
486
+ }
487
+ }
488
+
489
+ /**
490
+ * API to enable or disable TURN discovery
491
+ * @param {Boolean} enable
492
+ * @private
493
+ * @memberof Meetings
494
+ * @returns {undefined}
495
+ */
496
+ private _toggleTurnDiscovery(enable: boolean) {
497
+ if (typeof enable !== 'boolean') {
498
+ return;
499
+ }
500
+ // @ts-ignore
501
+ this.config.experimental.enableTurnDiscovery = enable;
502
+ }
503
+
504
+ /**
505
+ * API to toggle starting adhoc meeting
506
+ * @param {Boolean} changeState
507
+ * @private
508
+ * @memberof Meetings
509
+ * @returns {undefined}
510
+ */
511
+ private _toggleAdhocMeetings(changeState: boolean) {
512
+ if (typeof changeState !== 'boolean') {
513
+ return;
514
+ }
515
+ // @ts-ignore
516
+ if (this.config?.experimental?.enableAdhocMeetings !== changeState) {
517
+ // @ts-ignore
518
+ this.config.experimental.enableAdhocMeetings = changeState;
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Explicitly sets up the meetings plugin by registering
524
+ * the device, connecting to mercury, and listening for locus events.
525
+ *
526
+ * @returns {Promise}
527
+ * @public
528
+ * @memberof Meetings
529
+ */
530
+ public register() {
531
+ // @ts-ignore
532
+ if (!this.webex.canAuthorize) {
533
+ LoggerProxy.logger.error(
534
+ 'Meetings:index#register --> ERROR, Unable to register, SDK cannot authorize'
535
+ );
536
+
537
+ return Promise.reject(new Error('SDK cannot authorize'));
538
+ }
539
+
540
+ if (this.registered) {
541
+ LoggerProxy.logger.info(
542
+ 'Meetings:index#register --> INFO, Meetings plugin already registered'
543
+ );
544
+
545
+ return Promise.resolve();
546
+ }
547
+
548
+ return Promise.all([
549
+ this.fetchUserPreferredWebexSite(),
550
+ this.getGeoHint(),
551
+ this.startReachability().catch((error) => {
552
+ LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
553
+ }),
554
+ // @ts-ignore
555
+ this.webex.internal.device
556
+ .register()
557
+ // @ts-ignore
558
+ .then(() =>
559
+ LoggerProxy.logger.info(
560
+ // @ts-ignore
561
+ `Meetings:index#register --> INFO, Device registered ${this.webex.internal.device.url}`
562
+ )
563
+ )
564
+ // @ts-ignore
565
+ .then(() => this.webex.internal.mercury.connect()),
566
+ MeetingsUtil.checkH264Support.call(this),
567
+ ])
568
+ .then(() => {
569
+ this.listenForEvents();
570
+ Trigger.trigger(
571
+ this,
572
+ {
573
+ file: 'meetings',
574
+ function: 'register',
575
+ },
576
+ EVENT_TRIGGERS.MEETINGS_REGISTERED
577
+ );
578
+ this.registered = true;
579
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_SUCCESS);
580
+ })
581
+ .catch((error) => {
582
+ LoggerProxy.logger.error(
583
+ `Meetings:index#register --> ERROR, Unable to register, ${error.message}`
584
+ );
585
+
586
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETINGS_REGISTRATION_FAILED, {
587
+ reason: error.message,
588
+ stack: error.stack,
589
+ });
590
+
591
+ return Promise.reject(error);
592
+ });
593
+ }
594
+
595
+ /**
596
+ * Explicitly tears down the meetings plugin by deregistering
597
+ * the device, disconnecting from mercury, and stops listening to locus events
598
+ *
599
+ * @returns {Promise}
600
+ * @public
601
+ * @memberof Meetings
602
+ */
603
+ unregister() {
604
+ if (!this.registered) {
605
+ LoggerProxy.logger.info(
606
+ 'Meetings:index#unregister --> INFO, Meetings plugin already unregistered'
607
+ );
608
+
609
+ return Promise.resolve();
610
+ }
611
+
612
+ this.stopListeningForEvents();
613
+
614
+ return (
615
+ // @ts-ignore
616
+ this.webex.internal.mercury
617
+ .disconnect()
618
+ // @ts-ignore
619
+ .then(() => this.webex.internal.device.unregister())
620
+ .then(() => {
621
+ Trigger.trigger(
622
+ this,
623
+ {
624
+ file: 'meetings',
625
+ function: 'unregister',
626
+ },
627
+ EVENT_TRIGGERS.MEETINGS_UNREGISTERED
628
+ );
629
+ this.registered = false;
630
+ })
631
+ );
632
+ }
633
+
634
+ /**
635
+ * Uploads logs to the webex services for tracking
636
+ * @param {Object} [options={}]
637
+ * @param {String} [options.callStart] Call Start Time
638
+ * @param {String} [options.feedbackId] ID used for tracking
639
+ * @param {String} [options.locusId]
640
+ * @param {String} [options.correlationId]
641
+ * @param {String} [options.meetingId] webex meeting ID
642
+ * @param {String} [options.userId] userId
643
+ * @param {String} [options.orgId] org id
644
+ * @returns {String} feedback ID logs were submitted under
645
+ */
646
+ uploadLogs(
647
+ options: {
648
+ callStart?: string;
649
+ feedbackId?: string;
650
+ locusId?: string;
651
+ correlationId?: string;
652
+ meetingId?: string;
653
+ userId?: string;
654
+ orgId?: string;
655
+ } = {}
656
+ ) {
657
+ LoggerProxy.logger.info('Meetings:index#uploadLogs --> uploading logs');
658
+
659
+ return this.loggerRequest
660
+ .uploadLogs(options)
661
+ .then((uploadResult) => {
662
+ LoggerProxy.logger.info(
663
+ 'Meetings:index#uploadLogs --> Upload logs for meeting completed.',
664
+ uploadResult
665
+ );
666
+ Trigger.trigger(
667
+ this,
668
+ {
669
+ file: 'meetings',
670
+ function: 'uploadLogs',
671
+ },
672
+ EVENT_TRIGGERS.MEETING_LOG_UPLOAD_SUCCESS,
673
+ {
674
+ meetingId: options.meetingId,
675
+ details: uploadResult,
676
+ }
677
+ );
678
+
679
+ return uploadResult;
680
+ })
681
+ .catch((uploadError) => {
682
+ LoggerProxy.logger.error(
683
+ 'Meetings:index#uploadLogs --> Unable to upload logs for meeting',
684
+ uploadError
685
+ );
686
+ Trigger.trigger(
687
+ this,
688
+ {
689
+ file: 'meetings',
690
+ function: 'uploadLogs',
691
+ },
692
+ EVENT_TRIGGERS.MEETING_LOG_UPLOAD_FAILURE,
693
+ {
694
+ meetingId: options.meetingId,
695
+ reason: uploadError,
696
+ }
697
+ );
698
+
699
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.UPLOAD_LOGS_FAILURE, {
700
+ // @ts-ignore - seems like typo
701
+ meetingId: options.meetingsId,
702
+ reason: uploadError.message,
703
+ stack: uploadError.stack,
704
+ code: uploadError.code,
705
+ });
706
+ });
707
+ }
708
+
709
+ /**
710
+ * initializes the reachability instance for Meetings
711
+ * @returns {undefined}
712
+ * @public
713
+ * @memberof Meetings
714
+ */
715
+ setReachability() {
716
+ // @ts-ignore
717
+ this.reachability = new Reachability(this.webex);
718
+ }
719
+
720
+ /**
721
+ * gets the reachability instance for Meetings
722
+ * @returns {Reachability}
723
+ * @public
724
+ * @memberof Meetings
725
+ */
726
+ getReachability() {
727
+ return this.reachability;
728
+ }
729
+
730
+ /**
731
+ * initializes and starts gathering reachability for Meetings
732
+ * @returns {Promise}
733
+ * @public
734
+ * @memberof Meetings
735
+ */
736
+ startReachability() {
737
+ if (!this.reachability) {
738
+ this.setReachability();
739
+ }
740
+
741
+ return this.getReachability().gatherReachability();
742
+ }
743
+
744
+ /**
745
+ * Get geoHint for info for meetings
746
+ * @returns {Promise}
747
+ * @private
748
+ * @memberof Meetings
749
+ */
750
+ getGeoHint() {
751
+ return this.request.fetchGeoHint().then((res) => {
752
+ this.geoHintInfo = res;
753
+ });
754
+ }
755
+
756
+ /**
757
+ * Fetch user preferred webex site information
758
+ * This also has other infomation about the user
759
+ * @returns {Promise}
760
+ * @private
761
+ * @memberof Meetings
762
+ */
763
+ fetchUserPreferredWebexSite() {
764
+ return this.request.getMeetingPreferences().then((res) => {
765
+ if (res) {
766
+ this.preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
767
+ }
768
+ });
769
+ }
770
+
771
+ /**
772
+ * gets the personal meeting room instance, for saved PMR values for this user
773
+ * @returns {PersonalMeetingRoom}
774
+ * @public
775
+ * @memberof Meetings
776
+ */
777
+
778
+ getPersonalMeetingRoom() {
779
+ return this.personalMeetingRoom;
780
+ }
781
+
782
+ /**
783
+ * @param {Meeting} meeting
784
+ * @param {Object} reason
785
+ * @param {String} type
786
+ * @returns {Undefined}
787
+ * @private
788
+ * @memberof Meetings
789
+ */
790
+ private destroy(meeting: Meeting, reason: object) {
791
+ MeetingUtil.cleanUp(meeting);
792
+ this.meetingCollection.delete(meeting.id);
793
+ Trigger.trigger(
794
+ this,
795
+ {
796
+ file: 'meetings',
797
+ function: 'destroy',
798
+ },
799
+ EVENT_TRIGGERS.MEETING_REMOVED,
800
+ {
801
+ meetingId: meeting.id,
802
+ reason,
803
+ }
804
+ );
805
+ }
806
+
807
+ /**
808
+ * Create a meeting.
809
+ * @param {string} destination - sipURL, spaceId, phonenumber, or locus object}
810
+ * @param {string} [type] - the optional specified type, such as locusId
811
+ * @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
812
+ * @returns {Promise<Meeting>} A new Meeting.
813
+ * @public
814
+ * @memberof Meetings
815
+ */
816
+ public create(destination: string, type: string = null, useRandomDelayForInfo = false) {
817
+ // TODO: type should be from a dictionary
818
+
819
+ // Validate meeting information based on the provided destination and
820
+ // type. This must be performed prior to determining if the meeting is
821
+ // found in the collection, as we mutate the destination for hydra person
822
+ // id values.
823
+ return (
824
+ this.meetingInfo
825
+ .fetchInfoOptions(destination, type)
826
+ // Catch a failure to fetch info options.
827
+ .catch((error) => {
828
+ LoggerProxy.logger.info(
829
+ `Meetings:index#create --> INFO, unable to determine info options: ${error.message}`
830
+ );
831
+ })
832
+ .then((options: any = {}) => {
833
+ // Normalize the destination.
834
+ const targetDest = options.destination || destination;
835
+
836
+ // check for the conversation URL then sip Url
837
+ let meeting = null;
838
+
839
+ if (type === _CONVERSATION_URL_ || options.type === _CONVERSATION_URL_) {
840
+ const foundMeeting = this.meetingCollection.getByKey(CONVERSATION_URL, targetDest);
841
+
842
+ if (foundMeeting) {
843
+ const foundMeetingIsNotCalendarMeeting = !foundMeeting.locusInfo.scheduledMeeting;
844
+
845
+ // If the found meeting is not a calendar meeting, return that meeting.
846
+ // This allows for the creation of instant-meetings when calendar meetings are present.
847
+ if (foundMeetingIsNotCalendarMeeting) {
848
+ meeting = foundMeeting;
849
+ }
850
+ }
851
+ }
852
+
853
+ // Attempt to collect the meeting if it exists.
854
+ if (!meeting) {
855
+ meeting = this.meetingCollection.getByKey(SIP_URI, targetDest);
856
+ }
857
+
858
+ // Validate if a meeting was found.
859
+ if (!meeting) {
860
+ // Create a meeting based on the normalized destination and type.
861
+ return this.createMeeting(targetDest, type, useRandomDelayForInfo).then(
862
+ (createdMeeting: any) => {
863
+ // If the meeting was successfully created.
864
+ if (createdMeeting && createdMeeting.on) {
865
+ // Create a destruction event for the meeting.
866
+ createdMeeting.on(EVENTS.DESTROY_MEETING, (payload) => {
867
+ // @ts-ignore
868
+ if (this.config.autoUploadLogs) {
869
+ this.uploadLogs({
870
+ callStart: createdMeeting.locusInfo?.fullState?.lastActive,
871
+ correlationId: createdMeeting.correlationId,
872
+ feedbackId: createdMeeting.correlationId,
873
+ locusId: createdMeeting.locusId,
874
+ meetingId: createdMeeting.locusInfo?.info?.webExMeetingId,
875
+ }).then(() => this.destroy(createdMeeting, payload.reason));
876
+ } else {
877
+ this.destroy(createdMeeting, payload.reason);
878
+ }
879
+ });
880
+
881
+ createdMeeting.on(EVENTS.REQUEST_UPLOAD_LOGS, (meetingInstance) => {
882
+ // @ts-ignore
883
+ if (this.config.autoUploadLogs) {
884
+ this.uploadLogs({
885
+ callStart: meetingInstance?.locusInfo?.fullState?.lastActive,
886
+ correlationId: meetingInstance.correlationId,
887
+ feedbackId: meetingInstance.correlationId,
888
+ locusId: meetingInstance.locusId,
889
+ meetingId: meetingInstance.locusInfo?.info?.webExMeetingId,
890
+ });
891
+ }
892
+ });
893
+ } else {
894
+ LoggerProxy.logger.error(
895
+ `Meetings:index#create --> ERROR, meeting does not have on method, will not be destroyed, meeting cleanup impossible for meeting: ${meeting}`
896
+ );
897
+ }
898
+
899
+ // Return the newly created meeting.
900
+ return Promise.resolve(createdMeeting);
901
+ }
902
+ );
903
+ }
904
+
905
+ // Return the existing meeting.
906
+ return Promise.resolve(meeting);
907
+ })
908
+ );
909
+ }
910
+
911
+ /**
912
+ * @param {String} destination see create()
913
+ * @param {String} type see create()
914
+ * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
915
+ * @returns {Promise} a new meeting instance complete with meeting info and destination
916
+ * @private
917
+ * @memberof Meetings
918
+ */
919
+ private async createMeeting(
920
+ destination: any,
921
+ type: string = null,
922
+ useRandomDelayForInfo = false
923
+ ) {
924
+ const meeting = new Meeting(
925
+ {
926
+ // @ts-ignore
927
+ userId: this.webex.internal.device.userId,
928
+ // @ts-ignore
929
+ deviceUrl: this.webex.internal.device.url,
930
+ // @ts-ignore
931
+ orgId: this.webex.internal.device.orgId,
932
+ roapSeq: 0,
933
+ locus: type === _LOCUS_ID_ ? destination : null, // pass the locus object if present
934
+ meetingInfoProvider: this.meetingInfo,
935
+ destination,
936
+ destinationType: type,
937
+ },
938
+ {
939
+ // @ts-ignore
940
+ parent: this.webex,
941
+ }
942
+ );
943
+
944
+ this.meetingCollection.set(meeting);
945
+
946
+ try {
947
+ // if no participant has joined the scheduled meeting (meaning meeting is not active) and we get a locusEvent,
948
+ // it means the meeting will start in 5-6 min. In that case, we want to fetchMeetingInfo
949
+ // between 5 and 2 min (random between 3 minutes) before the meeting starts
950
+ // to avoid a spike in traffic to the wbxappi service
951
+ let waitingTime = 0;
952
+
953
+ if (destination.meeting) {
954
+ const {startTime} = destination.meeting;
955
+ const startTimeDate = new Date(startTime);
956
+ const startTimeDatestamp = startTimeDate.getTime();
957
+ const timeToStart = startTimeDatestamp - Date.now();
958
+ const maxWaitingTime = Math.max(
959
+ Math.min(timeToStart, MAX_RANDOM_DELAY_FOR_MEETING_INFO),
960
+ 0
961
+ );
962
+
963
+ waitingTime = Math.round(Math.random() * maxWaitingTime);
964
+ }
965
+ const isMeetingActive = !!destination.fullState?.active;
966
+ // @ts-ignore
967
+ const {enableUnifiedMeetings} = this.config.experimental;
968
+
969
+ if (enableUnifiedMeetings && !isMeetingActive && useRandomDelayForInfo && waitingTime > 0) {
970
+ meeting.fetchMeetingInfoTimeoutId = setTimeout(
971
+ () => meeting.fetchMeetingInfo({}),
972
+ waitingTime
973
+ );
974
+ meeting.parseMeetingInfo(undefined, destination);
975
+ } else {
976
+ await meeting.fetchMeetingInfo({});
977
+ }
978
+ } catch (err) {
979
+ if (!(err instanceof CaptchaError) && !(err instanceof PasswordError)) {
980
+ // if there is no meeting info we assume its a 1:1 call or wireless share
981
+ LoggerProxy.logger.info(
982
+ `Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
983
+ );
984
+ LoggerProxy.logger.info(
985
+ 'Meetings:index#createMeeting --> Info assuming this destination is a 1:1 or wireless share'
986
+ );
987
+ }
988
+ LoggerProxy.logger.debug(
989
+ `Meetings:index#createMeeting --> Debug ${err} fetching /meetingInfo for creation.`
990
+ );
991
+ } finally {
992
+ // For type LOCUS_ID we need to parse the locus object to get the information
993
+ // about the caller and callee
994
+ // Meeting Added event will be created in `handleLocusEvent`
995
+ if (type !== _LOCUS_ID_) {
996
+ if (!meeting.sipUri) {
997
+ meeting.setSipUri(destination);
998
+ }
999
+
1000
+ // TODO: check if we have to move this to parser
1001
+ const meetingAddedType = MeetingsUtil.getMeetingAddedType(type);
1002
+
1003
+ // We typically shouldn't need to trigger both and event and return a promise.
1004
+ // Is this a special case? We want to make the public API usage as simple as possible.
1005
+ Trigger.trigger(
1006
+ this,
1007
+ {
1008
+ file: 'meetings',
1009
+ function: 'createMeeting',
1010
+ },
1011
+ EVENT_TRIGGERS.MEETING_ADDED,
1012
+ {
1013
+ meeting,
1014
+ type: meetingAddedType,
1015
+ }
1016
+ );
1017
+ }
1018
+ }
1019
+
1020
+ return meeting;
1021
+
1022
+ // Create the meeting calling the necessary service endpoints.
1023
+
1024
+ // Internally, there are many more destinations:
1025
+ //
1026
+ // - locusID
1027
+ // - meetingURL
1028
+ // - globalMeetingID, e.g, *00*meetingID
1029
+ // - meetingID
1030
+ // - meetingURL
1031
+ // - PSTN
1032
+ // - phone number
1033
+ //
1034
+ // Our job is to determine the appropriate one
1035
+ // and its corresponding service so that developers
1036
+ // need only sipURL or spaceID to get a meeting
1037
+ // and its ID, but have the option to use createWithType()
1038
+ // and specify those types to get meetingInfo
1039
+ }
1040
+
1041
+ /**
1042
+ * get a specifc meeting given it's type matched to the value, i.e., locus url
1043
+ * @param {String} type
1044
+ * @param {Object} value
1045
+ * @returns {Meeting}
1046
+ * @public
1047
+ * @memberof Meetings
1048
+ */
1049
+ public getMeetingByType(type: string, value: object) {
1050
+ return this.meetingCollection.getByKey(type, value);
1051
+ }
1052
+
1053
+ /**
1054
+ * Get all meetings.
1055
+ * @param {object} options
1056
+ * @param {object} options.startDate - get meetings after this start date
1057
+ * @param {object} options.endDate - get meetings before this end date
1058
+ * @returns {Object} All currently active meetings.
1059
+ * @public
1060
+ * @memberof Meetings
1061
+ */
1062
+ public getAllMeetings(
1063
+ options: {
1064
+ startDate: object;
1065
+ endDate: object;
1066
+ } = {} as any
1067
+ ) {
1068
+ // Options may include other parameters to filter this collection
1069
+ // of meetings.
1070
+ return this.meetingCollection.getAll(options);
1071
+ }
1072
+
1073
+ /**
1074
+ * syncs all the meeting from server
1075
+ * @returns {undefined}
1076
+ * @public
1077
+ * @memberof Meetings
1078
+ */
1079
+ public syncMeetings() {
1080
+ return this.request.getActiveMeetings().then((locusArray) => {
1081
+ const activeLocusUrl = [];
1082
+
1083
+ if (locusArray?.loci && locusArray.loci.length > 0) {
1084
+ locusArray.loci.forEach((locus) => {
1085
+ activeLocusUrl.push(locus.url);
1086
+ this.handleLocusEvent({
1087
+ locus,
1088
+ locusUrl: locus.url,
1089
+ });
1090
+ });
1091
+ }
1092
+ const meetingsCollection = this.meetingCollection.getAll();
1093
+
1094
+ if (Object.keys(meetingsCollection).length > 0) {
1095
+ // Some time the mercury event is missed after mercury reconnect
1096
+ // if sync returns no locus then clear all the meetings
1097
+ for (const meeting of Object.values(meetingsCollection)) {
1098
+ // @ts-ignore
1099
+ if (!activeLocusUrl.includes(meeting.locusUrl)) {
1100
+ // destroy function also uploads logs
1101
+ // @ts-ignore
1102
+ this.destroy(meeting, MEETING_REMOVED_REASON.NO_MEETINGS_TO_SYNC);
1103
+ }
1104
+ }
1105
+ }
1106
+ });
1107
+ }
1108
+
1109
+ /**
1110
+ * Get all scheduled meetings.
1111
+ * @param {object} options
1112
+ * @param {object} options.startDate - get meetings after this start date
1113
+ * @param {object} options.endDate - get meetings before this end date
1114
+ * @returns {Object} All scheduled meetings.
1115
+ * @memberof Meetings
1116
+ */
1117
+ getScheduledMeetings() {
1118
+ return this.meetingCollection.getAll({scheduled: true});
1119
+ }
1120
+
1121
+ /**
1122
+ * Get the logger instance for plugin-meetings
1123
+ * @returns {Logger}
1124
+ */
1125
+ getLogger() {
1126
+ return LoggerProxy.get();
1127
+ }
1128
+ }