@webex/plugin-meetings 2.59.2 → 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 (418) 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 -116
  255. package/dist/statsAnalyzer/global.js +10 -0
  256. package/dist/statsAnalyzer/global.js.map +1 -1
  257. package/dist/statsAnalyzer/index.d.ts +190 -191
  258. package/dist/statsAnalyzer/index.js +137 -139
  259. package/dist/statsAnalyzer/index.js.map +1 -1
  260. package/dist/statsAnalyzer/mqaUtil.d.ts +22 -22
  261. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  262. package/dist/transcription/index.d.ts +64 -64
  263. package/dist/transcription/index.js +42 -42
  264. package/dist/transcription/index.js.map +1 -1
  265. package/internal-README.md +172 -172
  266. package/jest.config.js +3 -3
  267. package/package.json +22 -21
  268. package/process +1 -1
  269. package/src/common/browser-detection.ts +39 -39
  270. package/src/common/collection.ts +94 -94
  271. package/src/common/config.ts +9 -9
  272. package/src/common/errors/captcha-error.ts +25 -25
  273. package/src/common/errors/intent-to-join.ts +27 -27
  274. package/src/common/errors/join-meeting.ts +32 -32
  275. package/src/common/errors/media.ts +25 -25
  276. package/src/common/errors/parameter.ts +33 -33
  277. package/src/common/errors/password-error.ts +25 -25
  278. package/src/common/errors/permission.ts +24 -24
  279. package/src/common/errors/reconnection-in-progress.ts +8 -8
  280. package/src/common/errors/reconnection.ts +25 -25
  281. package/src/common/errors/stats.ts +25 -25
  282. package/src/common/errors/webex-errors.ts +140 -140
  283. package/src/common/errors/webex-meetings-error.ts +35 -35
  284. package/src/common/events/events-scope.ts +30 -30
  285. package/src/common/events/events.ts +25 -25
  286. package/src/common/events/trigger-proxy.ts +25 -25
  287. package/src/common/events/util.ts +39 -39
  288. package/src/common/logs/logger-config.ts +8 -8
  289. package/src/common/logs/logger-proxy.ts +44 -44
  290. package/src/common/logs/request.ts +65 -65
  291. package/src/common/queue.ts +50 -50
  292. package/src/config.ts +96 -96
  293. package/src/constants.ts +1121 -1121
  294. package/src/controls-options-manager/constants.ts +5 -5
  295. package/src/controls-options-manager/enums.ts +6 -6
  296. package/src/controls-options-manager/index.ts +183 -183
  297. package/src/controls-options-manager/util.ts +20 -20
  298. package/src/index.js +15 -15
  299. package/src/locus-info/controlsUtils.ts +112 -112
  300. package/src/locus-info/embeddedAppsUtils.ts +57 -57
  301. package/src/locus-info/fullState.ts +69 -69
  302. package/src/locus-info/hostUtils.ts +60 -60
  303. package/src/locus-info/index.ts +1303 -1303
  304. package/src/locus-info/infoUtils.ts +101 -101
  305. package/src/locus-info/mediaSharesUtils.ts +173 -173
  306. package/src/locus-info/parser.ts +680 -680
  307. package/src/locus-info/selfUtils.ts +428 -428
  308. package/src/media/index.ts +675 -675
  309. package/src/media/properties.ts +313 -313
  310. package/src/media/util.ts +37 -37
  311. package/src/mediaQualityMetrics/config.ts +382 -382
  312. package/src/meeting/effectsState.ts +209 -209
  313. package/src/meeting/in-meeting-actions.ts +153 -153
  314. package/src/meeting/index.ts +6537 -6543
  315. package/src/meeting/muteState.ts +365 -365
  316. package/src/meeting/request.ts +810 -810
  317. package/src/meeting/state.ts +194 -194
  318. package/src/meeting/util.ts +530 -530
  319. package/src/meeting-info/collection.ts +41 -41
  320. package/src/meeting-info/index.ts +137 -137
  321. package/src/meeting-info/meeting-info-v2.ts +273 -273
  322. package/src/meeting-info/request.ts +46 -46
  323. package/src/meeting-info/util.ts +314 -314
  324. package/src/meeting-info/utilv2.ts +324 -324
  325. package/src/meetings/collection.ts +43 -43
  326. package/src/meetings/index.ts +1128 -1128
  327. package/src/meetings/request.ts +81 -81
  328. package/src/meetings/util.ts +181 -181
  329. package/src/member/index.ts +446 -446
  330. package/src/member/member.types.ts +13 -13
  331. package/src/member/util.ts +286 -286
  332. package/src/members/collection.ts +40 -40
  333. package/src/members/index.ts +900 -900
  334. package/src/members/request.ts +175 -175
  335. package/src/members/util.ts +260 -260
  336. package/src/metrics/config.ts +485 -485
  337. package/src/metrics/constants.ts +61 -61
  338. package/src/metrics/index.ts +543 -543
  339. package/src/networkQualityMonitor/index.ts +211 -211
  340. package/src/peer-connection-manager/index.ts +847 -847
  341. package/src/peer-connection-manager/util.ts +119 -119
  342. package/src/personal-meeting-room/index.ts +157 -157
  343. package/src/personal-meeting-room/request.ts +48 -48
  344. package/src/personal-meeting-room/util.ts +49 -49
  345. package/src/reachability/index.ts +478 -478
  346. package/src/reachability/request.ts +81 -81
  347. package/src/reactions/reactions.ts +104 -104
  348. package/src/reactions/reactions.type.ts +36 -36
  349. package/src/reconnection-manager/index.ts +622 -622
  350. package/src/recording-controller/enums.ts +8 -8
  351. package/src/recording-controller/index.ts +315 -315
  352. package/src/recording-controller/util.ts +58 -58
  353. package/src/roap/collection.ts +62 -62
  354. package/src/roap/handler.ts +294 -294
  355. package/src/roap/index.ts +413 -413
  356. package/src/roap/request.ts +229 -229
  357. package/src/roap/state.ts +156 -156
  358. package/src/roap/turnDiscovery.ts +283 -283
  359. package/src/roap/util.ts +100 -100
  360. package/src/statsAnalyzer/global.ts +128 -118
  361. package/src/statsAnalyzer/index.ts +1266 -1265
  362. package/src/statsAnalyzer/mqaUtil.ts +290 -290
  363. package/src/transcription/index.ts +154 -154
  364. package/test/integration/spec/journey.js +941 -941
  365. package/test/integration/spec/space-meeting.js +457 -457
  366. package/test/integration/spec/transcription.js +55 -55
  367. package/test/unit/spec/common/browser-detection.js +119 -119
  368. package/test/unit/spec/common/queue.js +69 -69
  369. package/test/unit/spec/controls-options-manager/index.js +123 -123
  370. package/test/unit/spec/controls-options-manager/util.js +65 -65
  371. package/test/unit/spec/fixture/locus.js +406 -406
  372. package/test/unit/spec/locus-info/controlsUtils.js +82 -82
  373. package/test/unit/spec/locus-info/embeddedAppsUtils.js +104 -104
  374. package/test/unit/spec/locus-info/index.js +1272 -1272
  375. package/test/unit/spec/locus-info/infoUtils.js +138 -138
  376. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +975 -975
  377. package/test/unit/spec/locus-info/lib/SeqCmp.json +522 -522
  378. package/test/unit/spec/locus-info/lib/selfConstant.js +286 -286
  379. package/test/unit/spec/locus-info/parser.js +298 -298
  380. package/test/unit/spec/locus-info/selfUtils.js +185 -185
  381. package/test/unit/spec/media/properties.ts +305 -305
  382. package/test/unit/spec/meeting/effectsState.js +281 -281
  383. package/test/unit/spec/meeting/in-meeting-actions.ts +90 -90
  384. package/test/unit/spec/meeting/index.js +5227 -5227
  385. package/test/unit/spec/meeting/muteState.js +430 -430
  386. package/test/unit/spec/meeting/request.js +317 -317
  387. package/test/unit/spec/meeting/utils.js +319 -319
  388. package/test/unit/spec/meeting-info/meetinginfov2.js +376 -376
  389. package/test/unit/spec/meeting-info/request.js +64 -64
  390. package/test/unit/spec/meeting-info/util.js +37 -37
  391. package/test/unit/spec/meeting-info/utilv2.js +330 -330
  392. package/test/unit/spec/meetings/collection.js +52 -52
  393. package/test/unit/spec/meetings/index.js +1375 -1375
  394. package/test/unit/spec/meetings/utils.js +66 -66
  395. package/test/unit/spec/member/index.js +47 -47
  396. package/test/unit/spec/member/util.js +80 -80
  397. package/test/unit/spec/members/index.js +364 -364
  398. package/test/unit/spec/members/request.js +200 -200
  399. package/test/unit/spec/members/utils.js +42 -42
  400. package/test/unit/spec/metrics/index.js +111 -111
  401. package/test/unit/spec/networkQualityMonitor/index.js +99 -99
  402. package/test/unit/spec/peerconnection-manager/index.js +218 -218
  403. package/test/unit/spec/peerconnection-manager/utils.js +49 -49
  404. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +388 -388
  405. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +29 -29
  406. package/test/unit/spec/reachability/index.ts +50 -50
  407. package/test/unit/spec/reconnection-manager/index.js +206 -206
  408. package/test/unit/spec/recording-controller/index.js +230 -230
  409. package/test/unit/spec/recording-controller/util.js +101 -101
  410. package/test/unit/spec/roap/index.ts +128 -128
  411. package/test/unit/spec/roap/turnDiscovery.ts +372 -372
  412. package/test/unit/spec/roap/util.js +30 -30
  413. package/test/unit/spec/stats-analyzer/index.js +287 -355
  414. package/test/utils/cmr.js +104 -104
  415. package/test/utils/testUtils.js +287 -287
  416. package/test/utils/webex-config.js +77 -77
  417. package/test/utils/webex-test-users.js +82 -82
  418. 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
+ }