@webex/plugin-meetings 3.8.1 → 3.9.0-webinar5k.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 (296) hide show
  1. package/README.md +26 -13
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/constants.js +16 -3
  5. package/dist/constants.js.map +1 -1
  6. package/dist/controls-options-manager/enums.js +1 -0
  7. package/dist/controls-options-manager/enums.js.map +1 -1
  8. package/dist/controls-options-manager/types.js.map +1 -1
  9. package/dist/controls-options-manager/util.js +26 -0
  10. package/dist/controls-options-manager/util.js.map +1 -1
  11. package/dist/hashTree/constants.js +23 -0
  12. package/dist/hashTree/constants.js.map +1 -0
  13. package/dist/hashTree/hashTree.js +516 -0
  14. package/dist/hashTree/hashTree.js.map +1 -0
  15. package/dist/hashTree/hashTreeParser.js +521 -0
  16. package/dist/hashTree/hashTreeParser.js.map +1 -0
  17. package/dist/interpretation/index.js +1 -1
  18. package/dist/interpretation/siLanguage.js +1 -1
  19. package/dist/locus-info/controlsUtils.js +11 -3
  20. package/dist/locus-info/controlsUtils.js.map +1 -1
  21. package/dist/locus-info/index.js +331 -59
  22. package/dist/locus-info/index.js.map +1 -1
  23. package/dist/media/index.js +2 -2
  24. package/dist/media/index.js.map +1 -1
  25. package/dist/meeting/brbState.js +17 -14
  26. package/dist/meeting/brbState.js.map +1 -1
  27. package/dist/meeting/in-meeting-actions.js +5 -1
  28. package/dist/meeting/in-meeting-actions.js.map +1 -1
  29. package/dist/meeting/index.js +264 -125
  30. package/dist/meeting/index.js.map +1 -1
  31. package/dist/meeting/muteState.js +2 -5
  32. package/dist/meeting/muteState.js.map +1 -1
  33. package/dist/meeting/request.js +19 -0
  34. package/dist/meeting/request.js.map +1 -1
  35. package/dist/meeting/request.type.js.map +1 -1
  36. package/dist/meeting/util.js +8 -11
  37. package/dist/meeting/util.js.map +1 -1
  38. package/dist/meetings/index.js +6 -2
  39. package/dist/meetings/index.js.map +1 -1
  40. package/dist/member/types.js.map +1 -1
  41. package/dist/members/collection.js +13 -0
  42. package/dist/members/collection.js.map +1 -1
  43. package/dist/members/index.js +44 -23
  44. package/dist/members/index.js.map +1 -1
  45. package/dist/members/request.js +3 -3
  46. package/dist/members/request.js.map +1 -1
  47. package/dist/members/util.js +18 -6
  48. package/dist/members/util.js.map +1 -1
  49. package/dist/metrics/constants.js +1 -0
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/multistream/sendSlotManager.js +32 -2
  52. package/dist/multistream/sendSlotManager.js.map +1 -1
  53. package/dist/reachability/index.js +5 -10
  54. package/dist/reachability/index.js.map +1 -1
  55. package/dist/types/constants.d.ts +12 -0
  56. package/dist/types/controls-options-manager/enums.d.ts +2 -1
  57. package/dist/types/controls-options-manager/types.d.ts +4 -1
  58. package/dist/types/hashTree/constants.d.ts +8 -0
  59. package/dist/types/hashTree/hashTree.d.ts +128 -0
  60. package/dist/types/hashTree/hashTreeParser.d.ts +152 -0
  61. package/dist/types/locus-info/index.d.ts +93 -3
  62. package/dist/types/meeting/brbState.d.ts +0 -1
  63. package/dist/types/meeting/in-meeting-actions.d.ts +4 -0
  64. package/dist/types/meeting/index.d.ts +36 -3
  65. package/dist/types/meeting/request.d.ts +9 -1
  66. package/dist/types/meeting/request.type.d.ts +74 -0
  67. package/dist/types/meeting/util.d.ts +3 -3
  68. package/dist/types/member/types.d.ts +1 -0
  69. package/dist/types/members/collection.d.ts +6 -0
  70. package/dist/types/members/index.d.ts +15 -3
  71. package/dist/types/members/request.d.ts +1 -1
  72. package/dist/types/members/util.d.ts +5 -2
  73. package/dist/types/metrics/constants.d.ts +1 -0
  74. package/dist/types/multistream/sendSlotManager.d.ts +16 -0
  75. package/dist/types/reachability/index.d.ts +2 -2
  76. package/dist/webinar/index.js +1 -1
  77. package/package.json +26 -25
  78. package/src/constants.ts +16 -0
  79. package/src/controls-options-manager/enums.ts +1 -0
  80. package/src/controls-options-manager/types.ts +6 -1
  81. package/src/controls-options-manager/util.ts +31 -0
  82. package/src/hashTree/constants.ts +12 -0
  83. package/src/hashTree/hashTree.ts +460 -0
  84. package/src/hashTree/hashTreeParser.ts +556 -0
  85. package/src/locus-info/controlsUtils.ts +15 -0
  86. package/src/locus-info/index.ts +434 -58
  87. package/src/media/index.ts +2 -2
  88. package/src/meeting/brbState.ts +13 -9
  89. package/src/meeting/in-meeting-actions.ts +8 -0
  90. package/src/meeting/index.ts +193 -39
  91. package/src/meeting/muteState.ts +2 -6
  92. package/src/meeting/request.ts +16 -0
  93. package/src/meeting/request.type.ts +64 -0
  94. package/src/meeting/util.ts +17 -20
  95. package/src/meetings/index.ts +17 -3
  96. package/src/member/types.ts +1 -0
  97. package/src/members/collection.ts +11 -0
  98. package/src/members/index.ts +33 -7
  99. package/src/members/request.ts +2 -2
  100. package/src/members/util.ts +14 -3
  101. package/src/metrics/constants.ts +1 -0
  102. package/src/multistream/sendSlotManager.ts +34 -2
  103. package/src/reachability/index.ts +5 -13
  104. package/test/unit/spec/controls-options-manager/util.js +58 -0
  105. package/test/unit/spec/hashTree/hashTree.ts +394 -0
  106. package/test/unit/spec/hashTree/hashTreeParser.ts +156 -0
  107. package/test/unit/spec/locus-info/controlsUtils.js +52 -0
  108. package/test/unit/spec/locus-info/index.js +547 -54
  109. package/test/unit/spec/media/index.ts +107 -0
  110. package/test/unit/spec/meeting/brbState.ts +23 -4
  111. package/test/unit/spec/meeting/in-meeting-actions.ts +4 -0
  112. package/test/unit/spec/meeting/index.js +647 -46
  113. package/test/unit/spec/meeting/request.js +71 -0
  114. package/test/unit/spec/members/index.js +33 -10
  115. package/test/unit/spec/members/request.js +2 -2
  116. package/test/unit/spec/members/utils.js +27 -7
  117. package/test/unit/spec/multistream/sendSlotManager.ts +59 -0
  118. package/test/unit/spec/reachability/index.ts +2 -6
  119. package/dist/annotation/annotation.types.d.ts +0 -42
  120. package/dist/annotation/constants.d.ts +0 -31
  121. package/dist/annotation/index.d.ts +0 -117
  122. package/dist/breakouts/breakout.d.ts +0 -8
  123. package/dist/breakouts/collection.d.ts +0 -5
  124. package/dist/breakouts/edit-lock-error.d.ts +0 -15
  125. package/dist/breakouts/events.d.ts +0 -8
  126. package/dist/breakouts/index.d.ts +0 -5
  127. package/dist/breakouts/request.d.ts +0 -22
  128. package/dist/breakouts/utils.d.ts +0 -15
  129. package/dist/common/browser-detection.d.ts +0 -9
  130. package/dist/common/collection.d.ts +0 -48
  131. package/dist/common/config.d.ts +0 -2
  132. package/dist/common/errors/captcha-error.d.ts +0 -15
  133. package/dist/common/errors/intent-to-join.d.ts +0 -16
  134. package/dist/common/errors/join-meeting.d.ts +0 -17
  135. package/dist/common/errors/media.d.ts +0 -15
  136. package/dist/common/errors/no-meeting-info.d.ts +0 -14
  137. package/dist/common/errors/parameter.d.ts +0 -15
  138. package/dist/common/errors/password-error.d.ts +0 -15
  139. package/dist/common/errors/permission.d.ts +0 -14
  140. package/dist/common/errors/reclaim-host-role-error.d.ts +0 -60
  141. package/dist/common/errors/reclaim-host-role-error.js +0 -158
  142. package/dist/common/errors/reclaim-host-role-error.js.map +0 -1
  143. package/dist/common/errors/reclaim-host-role-errors.d.ts +0 -60
  144. package/dist/common/errors/reconnection-in-progress.d.ts +0 -9
  145. package/dist/common/errors/reconnection-in-progress.js +0 -35
  146. package/dist/common/errors/reconnection-in-progress.js.map +0 -1
  147. package/dist/common/errors/reconnection.d.ts +0 -15
  148. package/dist/common/errors/stats.d.ts +0 -15
  149. package/dist/common/errors/webex-errors.d.ts +0 -81
  150. package/dist/common/errors/webex-meetings-error.d.ts +0 -20
  151. package/dist/common/events/events-scope.d.ts +0 -17
  152. package/dist/common/events/events.d.ts +0 -12
  153. package/dist/common/events/trigger-proxy.d.ts +0 -2
  154. package/dist/common/events/util.d.ts +0 -2
  155. package/dist/common/logs/logger-config.d.ts +0 -2
  156. package/dist/common/logs/logger-proxy.d.ts +0 -2
  157. package/dist/common/logs/request.d.ts +0 -34
  158. package/dist/common/queue.d.ts +0 -32
  159. package/dist/config.d.ts +0 -73
  160. package/dist/constants.d.ts +0 -952
  161. package/dist/controls-options-manager/constants.d.ts +0 -4
  162. package/dist/controls-options-manager/enums.d.ts +0 -5
  163. package/dist/controls-options-manager/index.d.ts +0 -120
  164. package/dist/controls-options-manager/types.d.ts +0 -43
  165. package/dist/controls-options-manager/util.d.ts +0 -7
  166. package/dist/index.d.ts +0 -4
  167. package/dist/interceptors/index.d.ts +0 -2
  168. package/dist/interceptors/locusRetry.d.ts +0 -27
  169. package/dist/interpretation/collection.d.ts +0 -5
  170. package/dist/interpretation/index.d.ts +0 -5
  171. package/dist/interpretation/siLanguage.d.ts +0 -5
  172. package/dist/locus-info/controlsUtils.d.ts +0 -2
  173. package/dist/locus-info/embeddedAppsUtils.d.ts +0 -2
  174. package/dist/locus-info/fullState.d.ts +0 -2
  175. package/dist/locus-info/hostUtils.d.ts +0 -2
  176. package/dist/locus-info/index.d.ts +0 -269
  177. package/dist/locus-info/infoUtils.d.ts +0 -2
  178. package/dist/locus-info/mediaSharesUtils.d.ts +0 -2
  179. package/dist/locus-info/parser.d.ts +0 -212
  180. package/dist/locus-info/selfUtils.d.ts +0 -2
  181. package/dist/media/index.d.ts +0 -32
  182. package/dist/media/properties.d.ts +0 -108
  183. package/dist/media/util.d.ts +0 -2
  184. package/dist/mediaQualityMetrics/config.d.ts +0 -233
  185. package/dist/mediaQualityMetrics/config.js +0 -513
  186. package/dist/mediaQualityMetrics/config.js.map +0 -1
  187. package/dist/meeting/effectsState.d.ts +0 -42
  188. package/dist/meeting/effectsState.js +0 -260
  189. package/dist/meeting/effectsState.js.map +0 -1
  190. package/dist/meeting/in-meeting-actions.d.ts +0 -79
  191. package/dist/meeting/index.d.ts +0 -1622
  192. package/dist/meeting/locusMediaRequest.d.ts +0 -74
  193. package/dist/meeting/muteState.d.ts +0 -116
  194. package/dist/meeting/request.d.ts +0 -257
  195. package/dist/meeting/request.type.d.ts +0 -11
  196. package/dist/meeting/state.d.ts +0 -9
  197. package/dist/meeting/util.d.ts +0 -2
  198. package/dist/meeting/voicea-meeting.d.ts +0 -16
  199. package/dist/meeting-info/collection.d.ts +0 -20
  200. package/dist/meeting-info/index.d.ts +0 -57
  201. package/dist/meeting-info/meeting-info-v2.d.ts +0 -93
  202. package/dist/meeting-info/request.d.ts +0 -22
  203. package/dist/meeting-info/util.d.ts +0 -2
  204. package/dist/meeting-info/utilv2.d.ts +0 -2
  205. package/dist/meetings/collection.d.ts +0 -23
  206. package/dist/meetings/index.d.ts +0 -296
  207. package/dist/meetings/meetings.types.d.ts +0 -4
  208. package/dist/meetings/request.d.ts +0 -27
  209. package/dist/meetings/util.d.ts +0 -18
  210. package/dist/member/index.d.ts +0 -148
  211. package/dist/member/member.types.d.ts +0 -11
  212. package/dist/member/member.types.js +0 -18
  213. package/dist/member/member.types.js.map +0 -1
  214. package/dist/member/types.d.ts +0 -32
  215. package/dist/member/util.d.ts +0 -2
  216. package/dist/members/collection.d.ts +0 -24
  217. package/dist/members/index.d.ts +0 -308
  218. package/dist/members/request.d.ts +0 -58
  219. package/dist/members/types.d.ts +0 -25
  220. package/dist/members/util.d.ts +0 -2
  221. package/dist/metrics/config.d.ts +0 -169
  222. package/dist/metrics/config.js +0 -289
  223. package/dist/metrics/config.js.map +0 -1
  224. package/dist/metrics/constants.d.ts +0 -59
  225. package/dist/metrics/index.d.ts +0 -152
  226. package/dist/multistream/mediaRequestManager.d.ts +0 -119
  227. package/dist/multistream/receiveSlot.d.ts +0 -68
  228. package/dist/multistream/receiveSlotManager.d.ts +0 -56
  229. package/dist/multistream/remoteMedia.d.ts +0 -72
  230. package/dist/multistream/remoteMediaGroup.d.ts +0 -49
  231. package/dist/multistream/remoteMediaManager.d.ts +0 -300
  232. package/dist/multistream/sendSlotManager.d.ts +0 -69
  233. package/dist/networkQualityMonitor/index.d.ts +0 -70
  234. package/dist/networkQualityMonitor/index.js +0 -226
  235. package/dist/networkQualityMonitor/index.js.map +0 -1
  236. package/dist/peer-connection-manager/index.d.ts +0 -6
  237. package/dist/peer-connection-manager/index.js +0 -671
  238. package/dist/peer-connection-manager/index.js.map +0 -1
  239. package/dist/peer-connection-manager/util.d.ts +0 -6
  240. package/dist/peer-connection-manager/util.js +0 -110
  241. package/dist/peer-connection-manager/util.js.map +0 -1
  242. package/dist/personal-meeting-room/index.d.ts +0 -47
  243. package/dist/personal-meeting-room/request.d.ts +0 -14
  244. package/dist/personal-meeting-room/util.d.ts +0 -2
  245. package/dist/reachability/clusterReachability.d.ts +0 -109
  246. package/dist/reachability/index.d.ts +0 -139
  247. package/dist/reachability/request.d.ts +0 -35
  248. package/dist/reachability/util.d.ts +0 -8
  249. package/dist/reactions/constants.d.ts +0 -3
  250. package/dist/reactions/reactions.d.ts +0 -4
  251. package/dist/reactions/reactions.type.d.ts +0 -32
  252. package/dist/reconnection-manager/index.d.ts +0 -112
  253. package/dist/recording-controller/enums.d.ts +0 -7
  254. package/dist/recording-controller/index.d.ts +0 -193
  255. package/dist/recording-controller/util.d.ts +0 -13
  256. package/dist/roap/collection.d.ts +0 -10
  257. package/dist/roap/collection.js +0 -63
  258. package/dist/roap/collection.js.map +0 -1
  259. package/dist/roap/handler.d.ts +0 -47
  260. package/dist/roap/handler.js +0 -279
  261. package/dist/roap/handler.js.map +0 -1
  262. package/dist/roap/index.d.ts +0 -116
  263. package/dist/roap/request.d.ts +0 -35
  264. package/dist/roap/state.d.ts +0 -9
  265. package/dist/roap/state.js +0 -127
  266. package/dist/roap/state.js.map +0 -1
  267. package/dist/roap/turnDiscovery.d.ts +0 -81
  268. package/dist/roap/util.d.ts +0 -2
  269. package/dist/roap/util.js +0 -76
  270. package/dist/roap/util.js.map +0 -1
  271. package/dist/rtcMetrics/constants.d.ts +0 -4
  272. package/dist/rtcMetrics/constants.js +0 -11
  273. package/dist/rtcMetrics/constants.js.map +0 -1
  274. package/dist/rtcMetrics/index.d.ts +0 -61
  275. package/dist/rtcMetrics/index.js +0 -197
  276. package/dist/rtcMetrics/index.js.map +0 -1
  277. package/dist/statsAnalyzer/global.d.ts +0 -118
  278. package/dist/statsAnalyzer/global.js +0 -127
  279. package/dist/statsAnalyzer/global.js.map +0 -1
  280. package/dist/statsAnalyzer/index.d.ts +0 -193
  281. package/dist/statsAnalyzer/index.js +0 -1019
  282. package/dist/statsAnalyzer/index.js.map +0 -1
  283. package/dist/statsAnalyzer/mqaUtil.d.ts +0 -22
  284. package/dist/statsAnalyzer/mqaUtil.js +0 -181
  285. package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
  286. package/dist/transcription/index.d.ts +0 -64
  287. package/dist/types/common/errors/reconnection-in-progress.d.ts +0 -9
  288. package/dist/types/mediaQualityMetrics/config.d.ts +0 -241
  289. package/dist/types/networkQualityMonitor/index.d.ts +0 -70
  290. package/dist/types/rtcMetrics/constants.d.ts +0 -4
  291. package/dist/types/rtcMetrics/index.d.ts +0 -71
  292. package/dist/types/statsAnalyzer/global.d.ts +0 -36
  293. package/dist/types/statsAnalyzer/index.d.ts +0 -217
  294. package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
  295. package/dist/webinar/collection.d.ts +0 -16
  296. package/dist/webinar/index.d.ts +0 -5
@@ -1,3 +1,4 @@
1
+ /* eslint-disable class-methods-use-this */
1
2
  import {isEqual, assignWith, cloneDeep, isEmpty, forEach} from 'lodash';
2
3
 
3
4
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -30,6 +31,87 @@ import MediaSharesUtils from './mediaSharesUtils';
30
31
  import LocusDeltaParser from './parser';
31
32
  import Metrics from '../metrics';
32
33
  import BEHAVIORAL_METRICS from '../metrics/constants';
34
+ import HashTreeParser, {
35
+ DataSet,
36
+ HashTreeMessage,
37
+ HashTreeObject,
38
+ HtMeta,
39
+ LocusInfoUpdateType,
40
+ ObjectType,
41
+ } from '../hashTree/hashTreeParser';
42
+
43
+ export type LocusLLMEvent = {
44
+ data: {
45
+ eventType: 'locus.state_message';
46
+ stateElementsMessage: HashTreeMessage;
47
+ };
48
+ };
49
+
50
+ export type LocusDTO = {
51
+ controls?: any;
52
+ fullState?: {
53
+ active: boolean;
54
+ count: number;
55
+ lastActive: string;
56
+ locked: boolean;
57
+ sessionId: string;
58
+ seessionIds: string[];
59
+ startTime: number;
60
+ state: string;
61
+ type: string;
62
+ };
63
+ host?: {
64
+ id: string;
65
+ incomingCallProtocols: any[];
66
+ isExternal: boolean;
67
+ name: string;
68
+ orgId: string;
69
+ };
70
+ htMeta?: HtMeta;
71
+ info?: any;
72
+ jsSdkMeta?: {
73
+ removedParticipantIds: string[]; // list of ids of participants that are removed in the last update
74
+ };
75
+ links?: any;
76
+ mediaShares?: any[];
77
+ meetings?: any[];
78
+ participants: any[];
79
+ replaces?: any[];
80
+ self?: any;
81
+ sequence?: {
82
+ dirtyParticipants: number;
83
+ entries: number[];
84
+ rangeEnd: number;
85
+ rangeStart: number;
86
+ sequenceHash: number;
87
+ sessionToken: string;
88
+ since: string;
89
+ totalParticipants: number;
90
+ };
91
+ syncUrl?: string;
92
+ url?: string;
93
+ };
94
+
95
+ export type LocusApiResponseBody = {
96
+ dataSets?: DataSet[];
97
+ locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
98
+ };
99
+
100
+ const LocusDtoTopLevelKeys = [
101
+ 'controls',
102
+ 'fullState',
103
+ 'host',
104
+ 'info',
105
+ 'links',
106
+ 'mediaShares',
107
+ 'meetings',
108
+ 'participants',
109
+ 'replaces',
110
+ 'self',
111
+ 'sequence',
112
+ 'syncUrl',
113
+ 'url',
114
+ ];
33
115
 
34
116
  /**
35
117
  * @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
@@ -70,6 +152,9 @@ export default class LocusInfo extends EventsScope {
70
152
  resources: any;
71
153
  mainSessionLocusCache: any;
72
154
  self: any;
155
+ hashTreeParser?: HashTreeParser;
156
+ hashTreeObjectId2ParticipantId: Map<number, string>; // mapping of hash tree object ids to participant ids
157
+
73
158
  /**
74
159
  * Constructor
75
160
  * @param {function} updateMeeting callback to update the meeting object from an object
@@ -88,6 +173,7 @@ export default class LocusInfo extends EventsScope {
88
173
  this.meetingId = meetingId;
89
174
  this.updateMeeting = updateMeeting;
90
175
  this.locusParser = new LocusDeltaParser();
176
+ this.hashTreeObjectId2ParticipantId = new Map();
91
177
  }
92
178
 
93
179
  /**
@@ -99,6 +185,7 @@ export default class LocusInfo extends EventsScope {
99
185
  private doLocusSync(meeting: any) {
100
186
  let isDelta;
101
187
  let url;
188
+ let meetingDestroyed = false;
102
189
 
103
190
  if (this.locusParser.workingCopy.syncUrl) {
104
191
  url = this.locusParser.workingCopy.syncUrl;
@@ -134,35 +221,77 @@ export default class LocusInfo extends EventsScope {
134
221
 
135
222
  isDelta = false;
136
223
 
137
- return meeting.meetingRequest.getLocusDTO({url: meeting.locusUrl}).catch((err) => {
138
- LoggerProxy.logger.info(
139
- 'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
140
- );
141
- this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
142
- throw err;
143
- });
224
+ // Locus sometimes returns 403, for example if meeting has ended, no point trying the fallback to full sync in that case
225
+ if (e.statusCode !== 403) {
226
+ return meeting.meetingRequest.getLocusDTO({url: meeting.locusUrl}).catch((err) => {
227
+ LoggerProxy.logger.info(
228
+ 'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
229
+ );
230
+ this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
231
+ meetingDestroyed = true;
232
+ throw err;
233
+ });
234
+ }
235
+ LoggerProxy.logger.info(
236
+ 'Locus-info:index#doLocusSync --> got 403 from Locus, skipping fallback to full sync, destroying the meeting'
237
+ );
238
+ } else {
239
+ LoggerProxy.logger.info(
240
+ 'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
241
+ );
144
242
  }
145
- LoggerProxy.logger.info(
146
- 'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
147
- );
148
243
  this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
244
+ meetingDestroyed = true;
149
245
  throw e;
150
246
  })
151
247
  .then((res) => {
152
- if (isDelta) {
153
- if (!isEmpty(res.body)) {
154
- meeting.locusInfo.handleLocusDelta(res.body, meeting);
155
- } else {
248
+ if (isEmpty(res.body)) {
249
+ if (isDelta) {
156
250
  LoggerProxy.logger.info(
157
251
  'Locus-info:index#doLocusSync --> received empty body from syncUrl, so we already have latest Locus DTO'
158
252
  );
253
+ } else {
254
+ LoggerProxy.logger.info(
255
+ 'Locus-info:index#doLocusSync --> received empty body from full DTO sync request'
256
+ );
159
257
  }
160
- } else {
161
- meeting.locusInfo.onFullLocus(res.body);
258
+
259
+ return;
260
+ }
261
+
262
+ if (isDelta) {
263
+ if (res.body.baseSequence) {
264
+ meeting.locusInfo.handleLocusDelta(res.body, meeting); // todo: check if this is safe, is isDelta=true always only for non-hash tree locus
265
+
266
+ return;
267
+ }
268
+ // in some cases Locus might return us full DTO even when we asked for a delta
269
+ LoggerProxy.logger.info(
270
+ 'Locus-info:index#doLocusSync --> got full DTO when we asked for delta'
271
+ );
272
+ }
273
+ meeting.locusInfo.onFullLocus(res.body);
274
+ })
275
+ .catch((e) => {
276
+ LoggerProxy.logger.info(
277
+ `Locus-info:index#doLocusSync --> getLocusDTO succeeded but failed to handle result, locus parser will resume but not all data may be synced (${e.toString()})`
278
+ );
279
+
280
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LOCUS_SYNC_HANDLING_FAILED, {
281
+ correlationId: meeting.correlationId,
282
+ url,
283
+ reason: e.message,
284
+ errorName: e.name,
285
+ stack: e.stack,
286
+ code: e.code,
287
+ });
288
+ })
289
+ .finally(() => {
290
+ if (!meetingDestroyed) {
291
+ // Notify parser to resume processing delta events.
292
+ // Any deltas in the queue that have now been superseded by this sync will simply be ignored
293
+ this.locusParser.resume();
162
294
  }
163
- // Notify parser to resume processing delta events.
164
- // Any deltas in the queue that have now been superseded by this sync will simply be ignored
165
- this.locusParser.resume();
166
295
  });
167
296
  }
168
297
 
@@ -255,7 +384,7 @@ export default class LocusInfo extends EventsScope {
255
384
  this.updateLocusCache(locus);
256
385
  // above section only updates the locusInfo object
257
386
  // The below section makes sure it updates the locusInfo as well as updates the meeting object
258
- this.updateParticipants(locus.participants);
387
+ this.updateParticipants(locus.participants, []);
259
388
  // For 1:1 space meeting the conversation Url does not exist in locus.conversation
260
389
  this.updateConversationUrl(locus.conversationUrl, locus.info);
261
390
  this.updateControls(locus.controls, locus.self);
@@ -273,17 +402,190 @@ export default class LocusInfo extends EventsScope {
273
402
 
274
403
  /**
275
404
  * @param {Object} locus
405
+ * @param {DataSet[]} [dataSets=[]] - Array of data sets
276
406
  * @returns {undefined}
277
407
  * @memberof LocusInfo
278
408
  */
279
- initialSetup(locus: object) {
409
+ initialSetup(locus: object, dataSets: DataSet[] = []) {
280
410
  this.updateLocusCache(locus);
281
- this.onFullLocus(locus);
411
+ this.onFullLocus(locus, undefined, dataSets);
282
412
 
283
413
  // Change it to true after it receives it first locus object
284
414
  this.emitChange = true;
285
415
  }
286
416
 
417
+ /**
418
+ *
419
+ * @param {HashTreeObject} object data set object
420
+ * @param {any} locus
421
+ * @returns {void}
422
+ */
423
+ updateHashTreeObjectInLocus(object: HashTreeObject, locus: LocusDTO): LocusDTO {
424
+ const type = object.htMeta.elementId.type.toLowerCase();
425
+
426
+ switch (type) {
427
+ case ObjectType.locus: {
428
+ if (!object.data) {
429
+ LoggerProxy.logger.warn(
430
+ `Locus-info:index#updateHashTreeObjectInLocus --> received LOCUS object without data, this is not supported!`
431
+ );
432
+
433
+ return locus;
434
+ }
435
+ // replace the main locus
436
+
437
+ // The Locus object from MAIN dataset has empty participants, so removing them to avoid it overriding the ones in our current locus object
438
+ // Also, it doesn't have "self". That's OK as it won't override existing locus.self and also existing SDK code can handle that missing self in Locus updates
439
+ const locusObjectFromData = object.data;
440
+ delete locusObjectFromData.participants;
441
+
442
+ locus = {...locus, ...locusObjectFromData};
443
+ locus.htMeta = object.htMeta;
444
+ break;
445
+ }
446
+ case ObjectType.participant:
447
+ LoggerProxy.logger.info(
448
+ `Locus-info:index#updateHashTreeObjectInLocus --> participant id=${
449
+ object.htMeta.elementId.id
450
+ } ${object.data ? 'updated' : 'removed'}`
451
+ );
452
+ console.log(
453
+ 'marcin: hashTreeObjectId2ParticipantId=',
454
+ cloneDeep(this.hashTreeObjectId2ParticipantId)
455
+ );
456
+ if (object.data) {
457
+ if (!locus.participants) {
458
+ locus.participants = [];
459
+ }
460
+ const participantObject = object.data;
461
+ participantObject.htMeta = object.htMeta;
462
+ locus.participants.push(participantObject);
463
+ this.hashTreeObjectId2ParticipantId.set(object.htMeta.elementId.id, participantObject.id);
464
+ } else {
465
+ const participantId = this.hashTreeObjectId2ParticipantId.get(object.htMeta.elementId.id);
466
+
467
+ if (!locus.jsSdkMeta) {
468
+ locus.jsSdkMeta = {removedParticipantIds: []};
469
+ }
470
+ locus.jsSdkMeta.removedParticipantIds.push(participantId);
471
+ this.hashTreeObjectId2ParticipantId.delete(object.htMeta.elementId.id);
472
+ }
473
+ break;
474
+ case ObjectType.self:
475
+ if (!object.data) {
476
+ LoggerProxy.logger.warn(
477
+ `Locus-info:index#updateHashTreeObjectInLocus --> received SELF object without data, this is not supported!`
478
+ );
479
+
480
+ return locus;
481
+ }
482
+ locus.self = object.data;
483
+ break;
484
+ }
485
+
486
+ return locus;
487
+ }
488
+
489
+ /**
490
+ * Handles HTTP response from Locus API call when hash tree update.
491
+ * @param {Meeting} meeting meeting object
492
+ * @param {LocusApiResponseBody} responseBody body of the http reponse from Locus API call
493
+ * @returns {void}
494
+ */
495
+ handleLocusAPIResponse(meeting, responseBody: LocusApiResponseBody): void {
496
+ console.log('marcin: locus response from API call:', responseBody);
497
+ if (responseBody.dataSets) {
498
+ if (!this.hashTreeParser) {
499
+ LoggerProxy.logger.warn(
500
+ `Locus-info:index#handleLocusAPIResponse --> received response with hash tree info from Locus API, but we don't have the hashTreeParser created`
501
+ );
502
+
503
+ return;
504
+ }
505
+ // Locus is using the new hash tree mechanism
506
+ // so update our data in the hash tree parser
507
+ this.hashTreeParser.handleLocusUpdate(responseBody);
508
+
509
+ // but the Locus object we receive in this case looks same like classic delta, so we can use existing delta method to process it
510
+ this.onDeltaLocus(responseBody.locus);
511
+ } else {
512
+ // classic Locus delta
513
+ this.handleLocusDelta(responseBody.locus, meeting);
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Handles a hash tree message received from Locus.
519
+ *
520
+ * @param {Meeting} meeting - The meeting object
521
+ * @param {HashTreeMessage} message incoming hash tree message
522
+ * @returns {void}
523
+ */
524
+ private handleHashTreeMessage(meeting: any, message: HashTreeMessage) {
525
+ if (!this.hashTreeParser) {
526
+ LoggerProxy.logger.warn(
527
+ `Locus-info:index#handleHashTreeMessage --> received hash tree message, but we don't have the hashTreeParser`
528
+ );
529
+
530
+ return;
531
+ }
532
+ if (message.locusStateElements === undefined) {
533
+ // todo: need to see in practice how exactly the heartbeat messages look like
534
+ this.hashTreeParser.handleRootHashHeartBeatMessage(message);
535
+ } else {
536
+ this.hashTreeParser.handleMessage(message);
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Updates our locus info based on the data parsed by the hash tree parser.
542
+ *
543
+ * @param {LocusInfoUpdateType} updateType - The type of update received.
544
+ * @param {Object} [data] - Additional data for the update, if applicable.
545
+ * @returns {void}
546
+ */
547
+ private updateFromHashTree(
548
+ updateType: LocusInfoUpdateType,
549
+ data?: {updatedObjects: HashTreeObject[]}
550
+ ) {
551
+ switch (updateType) {
552
+ case LocusInfoUpdateType.OBJECTS_UPDATED: {
553
+ // initialize the main locus with what we currently have
554
+ // but with empty participants array
555
+ let locus: LocusDTO = {
556
+ participants: [],
557
+ jsSdkMeta: {removedParticipantIds: []},
558
+ };
559
+
560
+ LocusDtoTopLevelKeys.forEach((key) => {
561
+ if (key === 'participants') {
562
+ locus[key] = [];
563
+ } else {
564
+ locus[key] = cloneDeep(this[key]);
565
+ }
566
+ });
567
+
568
+ // apply the updates from the hash tree onto the locus
569
+ data.updatedObjects.forEach((object) => {
570
+ locus = this.updateHashTreeObjectInLocus(object, locus);
571
+ });
572
+
573
+ // update our locus info with the new locus
574
+ this.onDeltaLocus(locus);
575
+
576
+ break;
577
+ }
578
+
579
+ case LocusInfoUpdateType.MEETING_ENDED: {
580
+ LoggerProxy.logger.info(
581
+ `Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
582
+ );
583
+ const meeting = this.webex.meetings.meetingCollection.get(this.meetingId);
584
+ this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
585
+ }
586
+ }
587
+ }
588
+
287
589
  /**
288
590
  * @param {Meeting} meeting
289
591
  * @param {Object} data
@@ -291,36 +593,43 @@ export default class LocusInfo extends EventsScope {
291
593
  * @memberof LocusInfo
292
594
  */
293
595
  parse(meeting: any, data: any) {
294
- // eslint-disable-next-line @typescript-eslint/no-shadow
295
- const {eventType} = data;
296
- const locus = this.getTheLocusToUpdate(data.locus);
297
- LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
298
-
299
- switch (eventType) {
300
- case LOCUSEVENT.PARTICIPANT_JOIN:
301
- case LOCUSEVENT.PARTICIPANT_LEFT:
302
- case LOCUSEVENT.CONTROLS_UPDATED:
303
- case LOCUSEVENT.PARTICIPANT_AUDIO_MUTED:
304
- case LOCUSEVENT.PARTICIPANT_AUDIO_UNMUTED:
305
- case LOCUSEVENT.PARTICIPANT_VIDEO_MUTED:
306
- case LOCUSEVENT.PARTICIPANT_VIDEO_UNMUTED:
307
- case LOCUSEVENT.SELF_CHANGED:
308
- case LOCUSEVENT.PARTICIPANT_UPDATED:
309
- case LOCUSEVENT.PARTICIPANT_CONTROLS_UPDATED:
310
- case LOCUSEVENT.PARTICIPANT_ROLES_UPDATED:
311
- case LOCUSEVENT.PARTICIPANT_DECLINED:
312
- case LOCUSEVENT.FLOOR_GRANTED:
313
- case LOCUSEVENT.FLOOR_RELEASED:
314
- this.onFullLocus(locus, eventType);
315
- break;
316
- case LOCUSEVENT.DIFFERENCE:
317
- this.handleLocusDelta(locus, meeting);
318
- break;
319
-
320
- default:
321
- // Why will there be a event with no eventType ????
322
- // we may not need this, we can get full locus
323
- this.handleLocusDelta(locus, meeting);
596
+ if (data.eventType === 'locus.state_message') {
597
+ // this is the new hashmap Locus message format (only applicable to webinars for now)
598
+ this.handleHashTreeMessage(meeting, data.stateElementsMessage as HashTreeMessage);
599
+ } else {
600
+ // eslint-disable-next-line @typescript-eslint/no-shadow
601
+ const {eventType} = data;
602
+ const locus = this.getTheLocusToUpdate(data.locus);
603
+ LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
604
+
605
+ locus.jsSdkMeta = {removedParticipantIds: []};
606
+
607
+ switch (eventType) {
608
+ case LOCUSEVENT.PARTICIPANT_JOIN:
609
+ case LOCUSEVENT.PARTICIPANT_LEFT:
610
+ case LOCUSEVENT.CONTROLS_UPDATED:
611
+ case LOCUSEVENT.PARTICIPANT_AUDIO_MUTED:
612
+ case LOCUSEVENT.PARTICIPANT_AUDIO_UNMUTED:
613
+ case LOCUSEVENT.PARTICIPANT_VIDEO_MUTED:
614
+ case LOCUSEVENT.PARTICIPANT_VIDEO_UNMUTED:
615
+ case LOCUSEVENT.SELF_CHANGED:
616
+ case LOCUSEVENT.PARTICIPANT_UPDATED:
617
+ case LOCUSEVENT.PARTICIPANT_CONTROLS_UPDATED:
618
+ case LOCUSEVENT.PARTICIPANT_ROLES_UPDATED:
619
+ case LOCUSEVENT.PARTICIPANT_DECLINED:
620
+ case LOCUSEVENT.FLOOR_GRANTED:
621
+ case LOCUSEVENT.FLOOR_RELEASED:
622
+ this.onFullLocus(locus, eventType);
623
+ break;
624
+ case LOCUSEVENT.DIFFERENCE:
625
+ this.handleLocusDelta(locus, meeting);
626
+ break;
627
+
628
+ default:
629
+ // Why will there be a event with no eventType ????
630
+ // we may not need this, we can get full locus
631
+ this.handleLocusDelta(locus, meeting);
632
+ }
324
633
  }
325
634
  }
326
635
 
@@ -339,17 +648,46 @@ export default class LocusInfo extends EventsScope {
339
648
  * updates the locus with full locus object
340
649
  * @param {object} locus locus object
341
650
  * @param {string} eventType particulat locus event
651
+ * @param {DataSet[]} dataSets
342
652
  * @returns {object} null
343
653
  * @memberof LocusInfo
344
654
  */
345
- onFullLocus(locus: any, eventType?: string) {
655
+ onFullLocus(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
346
656
  if (!locus) {
347
657
  LoggerProxy.logger.error(
348
658
  'Locus-info:index#onFullLocus --> object passed as argument was invalid, continuing.'
349
659
  );
350
660
  }
351
661
 
352
- if (!this.locusParser.isNewFullLocus(locus)) {
662
+ if (dataSets) {
663
+ // this is the new hashmap Locus DTO format (only applicable to webinars for now)
664
+ if (!this.hashTreeParser) {
665
+ LoggerProxy.logger.info(`Locus-info:index#onFullLocus --> creating hash tree parser`);
666
+ LoggerProxy.logger.info(
667
+ 'Locus-info:index#onFullLocus --> dataSets:',
668
+ dataSets,
669
+ ' and locus:',
670
+ locus
671
+ );
672
+ this.hashTreeParser = new HashTreeParser({
673
+ initialLocus: {locus, dataSets},
674
+ webexRequest: this.webex.request.bind(this.webex),
675
+ locusInfoUpdateCallback: this.updateFromHashTree.bind(this),
676
+ debugId: `HT-${this.meetingId.substring(0, 4)}`,
677
+ });
678
+ } else {
679
+ // in this case the Locus we're getting is not necessarily the full one
680
+ // so treat it like if we just got it in a message
681
+ console.log('marcin: !!!!!!!! full DTO - this is not fully implemented/tested yet');
682
+
683
+ LoggerProxy.logger.warn(
684
+ 'Locus-info:index#onFullLocus --> full DTO - this is not fully implemented/tested yet!!!!!!!!'
685
+ );
686
+ this.handleLocusAPIResponse(undefined, {dataSets, locus});
687
+
688
+ return;
689
+ }
690
+ } else if (!this.locusParser.isNewFullLocus(locus)) {
353
691
  LoggerProxy.logger.info(
354
692
  `Locus-info:index#onFullLocus --> ignoring old full locus DTO, eventType=${eventType}`
355
693
  );
@@ -360,9 +698,16 @@ export default class LocusInfo extends EventsScope {
360
698
  this.updateParticipantDeltas(locus.participants);
361
699
  this.scheduledMeeting = locus.meeting || null;
362
700
  this.participants = locus.participants;
701
+ this.participants?.forEach((participant) => {
702
+ this.hashTreeObjectId2ParticipantId.set(participant.htMeta.elementId.id, participant.id);
703
+ });
363
704
  const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
364
705
  this.updateLocusInfo(locus);
365
- this.updateParticipants(locus.participants, isReplaceMembers);
706
+ this.updateParticipants(
707
+ locus.participants,
708
+ locus.jsSdkMeta?.removedParticipantIds,
709
+ isReplaceMembers
710
+ );
366
711
  this.isMeetingActive();
367
712
  this.handleOneOnOneEvent(eventType);
368
713
  this.updateEmbeddedApps(locus.embeddedApps);
@@ -424,7 +769,11 @@ export default class LocusInfo extends EventsScope {
424
769
  const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
425
770
  this.mergeParticipants(this.participants, locus.participants);
426
771
  this.updateLocusInfo(locus);
427
- this.updateParticipants(locus.participants, isReplaceMembers);
772
+ this.updateParticipants(
773
+ locus.participants,
774
+ locus.jsSdkMeta?.removedParticipantIds,
775
+ isReplaceMembers
776
+ );
428
777
  this.isMeetingActive();
429
778
  }
430
779
 
@@ -446,12 +795,12 @@ export default class LocusInfo extends EventsScope {
446
795
  this.updateCreated(locus.created);
447
796
  this.updateFullState(locus.fullState);
448
797
  this.updateHostInfo(locus.host);
798
+ this.updateLocusUrl(locus.url);
449
799
  this.updateMeetingInfo(locus.info, locus.self);
450
800
  this.updateMediaShares(locus.mediaShares);
451
801
  this.updateParticipantsUrl(locus.participantsUrl);
452
802
  this.updateReplace(locus.replace);
453
803
  this.updateSelf(locus.self);
454
- this.updateLocusUrl(locus.url);
455
804
  this.updateAclUrl(locus.aclUrl);
456
805
  this.updateBasequence(locus.baseSequence);
457
806
  this.updateSequence(locus.sequence);
@@ -764,11 +1113,12 @@ export default class LocusInfo extends EventsScope {
764
1113
  /**
765
1114
  * update meeting's members
766
1115
  * @param {Object} participants new participants object
1116
+ * @param {Array} removedParticipantIds list of removed participants
767
1117
  * @param {Boolean} isReplace is replace the whole members
768
1118
  * @returns {Array} updatedParticipants
769
1119
  * @memberof LocusInfo
770
1120
  */
771
- updateParticipants(participants: object, isReplace?: boolean) {
1121
+ updateParticipants(participants: object, removedParticipantIds: string[], isReplace?: boolean) {
772
1122
  this.emitScoped(
773
1123
  {
774
1124
  file: 'locus-info',
@@ -777,6 +1127,7 @@ export default class LocusInfo extends EventsScope {
777
1127
  EVENTS.LOCUS_INFO_UPDATE_PARTICIPANTS,
778
1128
  {
779
1129
  participants,
1130
+ removedParticipantIds,
780
1131
  recordingId: this.parsedLocus.controls && this.parsedLocus.controls.record?.modifiedBy,
781
1132
  selfIdentity: this.parsedLocus.self && this.parsedLocus.self.selfIdentity,
782
1133
  selfId: this.parsedLocus.self && this.parsedLocus.self.selfId,
@@ -818,6 +1169,7 @@ export default class LocusInfo extends EventsScope {
818
1169
  hasRecordingPausedChanged,
819
1170
  hasMeetingContainerChanged,
820
1171
  hasTranscribeChanged,
1172
+ hasTranscribeSpokenLanguageChanged,
821
1173
  hasManualCaptionChanged,
822
1174
  hasEntryExitToneChanged,
823
1175
  hasBreakoutChanged,
@@ -837,6 +1189,7 @@ export default class LocusInfo extends EventsScope {
837
1189
  hasStageViewChanged,
838
1190
  hasAnnotationControlChanged,
839
1191
  hasRemoteDesktopControlChanged,
1192
+ hasPollingQAControlChanged,
840
1193
  },
841
1194
  current,
842
1195
  } = ControlsUtils.getControls(this.controls, controls);
@@ -956,6 +1309,21 @@ export default class LocusInfo extends EventsScope {
956
1309
  );
957
1310
  }
958
1311
 
1312
+ if (hasTranscribeSpokenLanguageChanged) {
1313
+ const {spokenLanguage} = current.transcribe;
1314
+
1315
+ this.emitScoped(
1316
+ {
1317
+ file: 'locus-info',
1318
+ function: 'updateControls',
1319
+ },
1320
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
1321
+ {
1322
+ spokenLanguage,
1323
+ }
1324
+ );
1325
+ }
1326
+
959
1327
  if (hasManualCaptionChanged) {
960
1328
  const {enabled} = current.manualCaptionControl;
961
1329
 
@@ -1088,6 +1456,14 @@ export default class LocusInfo extends EventsScope {
1088
1456
  );
1089
1457
  }
1090
1458
 
1459
+ if (hasPollingQAControlChanged) {
1460
+ this.emitScoped(
1461
+ {file: 'locus-info', function: 'updateControls'},
1462
+ LOCUSINFO.EVENTS.CONTROLS_POLLING_QA_CHANGED,
1463
+ {state: current.pollingQAControl}
1464
+ );
1465
+ }
1466
+
1091
1467
  this.controls = controls;
1092
1468
  }
1093
1469
  }
@@ -239,8 +239,8 @@ Media.createMediaConnection = (
239
239
  screenShareAudio: shareAudioStream?.outputStream?.getTracks()[0], // TODO: add type for screenShareAudio in internal-media-core SPARK-446923
240
240
  } as unknown,
241
241
  direction: {
242
- audio: Media.getDirection(true, mediaDirection.receiveAudio, mediaDirection.sendAudio),
243
- video: Media.getDirection(true, mediaDirection.receiveVideo, mediaDirection.sendVideo),
242
+ audio: Media.getDirection(false, mediaDirection.receiveAudio, mediaDirection.sendAudio),
243
+ video: Media.getDirection(false, mediaDirection.receiveVideo, mediaDirection.sendVideo),
244
244
  screenShareVideo: Media.getDirection(
245
245
  false,
246
246
  mediaDirection.receiveShare,