@@ -73,6 +73,7 @@ export class SubconversationService extends TypedEventEmitter<Events> {
7373 shouldRetry = true ,
7474 ) : Promise < { groupId : string ; epoch : number } > {
7575 try {
76+ this . logger . info ( 'Joining conference subconversation' , { conversationId, groupId} ) ;
7677 const {
7778 group_id : subconversationGroupId ,
7879 epoch : subconversationEpoch ,
@@ -113,6 +114,7 @@ export class SubconversationService extends TypedEventEmitter<Events> {
113114
114115 return { groupId : subconversationGroupId , epoch} ;
115116 } catch ( error ) {
117+ this . logger . error ( 'Failed to join conference subconversation' , { conversationId, groupId, error, shouldRetry} ) ;
116118 if ( shouldRetry ) {
117119 return this . joinConferenceSubconversation ( conversationId , groupId , false ) ;
118120 }
@@ -126,34 +128,55 @@ export class SubconversationService extends TypedEventEmitter<Events> {
126128 * @param conversationId Id of the parent conversation which subconversation we want to leave
127129 */
128130 public async leaveConferenceSubconversation ( conversationId : QualifiedId ) : Promise < void > {
131+ this . logger . info ( 'Leaving conference subconversation' , { conversationId} ) ;
129132 const subconversationGroupId = await this . getSubconversationGroupId ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
130133
131134 if ( ! subconversationGroupId ) {
135+ this . logger . warn ( 'No subconversation groupId found when leaving conference subconversation' , { conversationId} ) ;
132136 return ;
133137 }
134138
135139 const doesGroupExistLocally = await this . mlsService . conversationExists ( subconversationGroupId ) ;
136140 if ( ! doesGroupExistLocally ) {
137141 // If the subconversation was known by a client but is does not exist locally, we can remove it from the store.
142+ this . logger . info ( 'Subconversation not found locally; clearing stored mapping' , {
143+ conversationId,
144+ subconversationId : SUBCONVERSATION_ID . CONFERENCE ,
145+ subconversationGroupId,
146+ } ) ;
138147 return this . clearSubconversationGroupId ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
139148 }
140149
141150 try {
142151 await this . apiClient . api . conversation . deleteSubconversationSelf ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
143152 } catch ( error ) {
144- this . logger . error ( `Failed to leave conference subconversation:` , error ) ;
153+ this . logger . error ( 'Failed to leave conference subconversation' , {
154+ conversationId,
155+ subconversationId : SUBCONVERSATION_ID . CONFERENCE ,
156+ subconversationGroupId,
157+ error,
158+ } ) ;
145159 }
146160
147161 await this . mlsService . wipeConversation ( subconversationGroupId ) ;
148162
149163 // once we've left the subconversation, we can remove it from the store
164+ this . logger . info ( 'Clearing stored mapping after leaving conference subconversation' , {
165+ conversationId,
166+ subconversationId : SUBCONVERSATION_ID . CONFERENCE ,
167+ subconversationGroupId,
168+ } ) ;
150169 await this . clearSubconversationGroupId ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
151170 }
152171
153172 public async leaveStaleConferenceSubconversations ( ) : Promise < void > {
173+ this . logger . info ( 'Leaving all stale conference subconversations' ) ;
154174 const conversationIds = await this . getAllGroupIdsBySubconversationId ( SUBCONVERSATION_ID . CONFERENCE ) ;
155175
156176 for ( const { parentConversationId} of conversationIds ) {
177+ this . logger . debug ( 'Leaving stale conference subconversation for parent conversation' , {
178+ parentConversationId,
179+ } ) ;
157180 await this . leaveConferenceSubconversation ( parentConversationId ) ;
158181 }
159182 }
@@ -168,38 +191,55 @@ export class SubconversationService extends TypedEventEmitter<Events> {
168191 secretKey : string ;
169192 keyLength : number ;
170193 } | null > {
194+ this . logger . info ( 'Getting subconversation epoch info' , {
195+ parentConversationId,
196+ parentConversationGroupId,
197+ shouldAdvanceEpoch,
198+ } ) ;
171199 const subconversationGroupId = await this . getSubconversationGroupId (
172200 parentConversationId ,
173201 SUBCONVERSATION_ID . CONFERENCE ,
174202 ) ;
175203
176204 // this method should not be called if the subconversation (and its parent conversation) is not established
177205 if ( ! subconversationGroupId ) {
178- this . logger . error (
179- `Could not obtain epoch info for conference subconversation of conversation ${ JSON . stringify (
180- parentConversationId ,
181- ) } : parent or subconversation group ID is missing`,
182- ) ;
206+ this . logger . error ( 'Could not obtain epoch info for conference subconversation: missing groupId' , {
207+ parentConversationId,
208+ } ) ;
183209
184210 return null ;
185211 }
186212
187213 //we don't want to react to avs callbacks when conversation was not yet established
188214 const doesMLSGroupExist = await this . mlsService . conversationExists ( subconversationGroupId ) ;
189215 if ( ! doesMLSGroupExist ) {
216+ this . logger . debug ( 'Subconversation MLS group does not exist locally; skipping epoch info' , {
217+ parentConversationId,
218+ parentConversationGroupId,
219+ subconversationGroupId,
220+ } ) ;
190221 return null ;
191222 }
192223
193224 const members = await this . generateSubconversationMembers ( subconversationGroupId , parentConversationGroupId ) ;
194225
195226 if ( shouldAdvanceEpoch ) {
227+ this . logger . info ( 'Advancing epoch and renewing key material for subconversation' , { subconversationGroupId} ) ;
196228 await this . mlsService . renewKeyMaterial ( subconversationGroupId ) ;
197229 }
198230
199231 const epoch = Number ( await this . mlsService . getEpoch ( subconversationGroupId ) ) ;
200232
201233 const secretKey = await this . mlsService . exportSecretKey ( subconversationGroupId , MLS_CONVERSATION_KEY_LENGTH ) ;
202234
235+ this . logger . debug ( 'Obtained subconversation epoch info' , {
236+ parentConversationId,
237+ parentConversationGroupId,
238+ subconversationGroupId,
239+ epoch,
240+ membersCount : members . length ,
241+ keyLength : MLS_CONVERSATION_KEY_LENGTH ,
242+ } ) ;
203243 return { members, epoch, keyLength : MLS_CONVERSATION_KEY_LENGTH , secretKey} ;
204244 }
205245
@@ -214,16 +254,22 @@ export class SubconversationService extends TypedEventEmitter<Events> {
214254 keyLength : number ;
215255 } ) => void ,
216256 ) : Promise < ( ) => void > {
257+ this . logger . info ( 'Subscribing to subconversation epoch updates' , {
258+ parentConversationId,
259+ parentConversationGroupId,
260+ } ) ;
217261 const { epoch : initialEpoch , groupId : subconversationGroupId } = await this . joinConferenceSubconversation (
218262 parentConversationId ,
219263 parentConversationGroupId ,
220264 ) ;
221265
222266 const forwardNewEpoch = async ( { groupId} : { groupId : string ; epoch : number } ) => {
267+ this . logger . debug ( 'Received MLS NEW_EPOCH event' , { eventGroupId : groupId , subconversationGroupId} ) ;
223268 if ( groupId !== subconversationGroupId ) {
224269 // if the epoch update did not happen in the subconversation directly, check if it happened in the parent conversation
225270 const parentConversationId = findConversationByGroupId ( groupId ) ;
226271 if ( ! parentConversationId ) {
272+ this . logger . debug ( 'Ignoring NEW_EPOCH event: could not map to parent conversation' ) ;
227273 return ;
228274 }
229275
@@ -234,6 +280,11 @@ export class SubconversationService extends TypedEventEmitter<Events> {
234280
235281 // if the conference subconversation of parent conversation is not known, ignore the epoch update
236282 if ( foundSubconversationGroupId !== subconversationGroupId ) {
283+ this . logger . debug ( 'Ignoring NEW_EPOCH event: not related to subscribed subconversation' , {
284+ eventGroupId : groupId ,
285+ foundSubconversationGroupId,
286+ expectedSubconversationGroupId : subconversationGroupId ,
287+ } ) ;
237288 return ;
238289 }
239290 }
@@ -244,11 +295,19 @@ export class SubconversationService extends TypedEventEmitter<Events> {
244295 ) ;
245296
246297 if ( ! subconversationEpochInfo ) {
298+ this . logger . debug ( 'No subconversation epoch info available; skipping callback' , {
299+ parentConversationId,
300+ parentConversationGroupId,
301+ } ) ;
247302 return ;
248303 }
249304
250305 const newSubconversationEpoch = Number ( await this . mlsService . getEpoch ( subconversationGroupId ) ) ;
251306
307+ this . logger . info ( 'Forwarding epoch update to subscriber' , {
308+ subconversationGroupId,
309+ epoch : newSubconversationEpoch ,
310+ } ) ;
252311 return onEpochUpdate ( {
253312 ...subconversationEpochInfo ,
254313 epoch : newSubconversationEpoch ,
@@ -259,21 +318,32 @@ export class SubconversationService extends TypedEventEmitter<Events> {
259318
260319 await forwardNewEpoch ( { groupId : subconversationGroupId , epoch : initialEpoch } ) ;
261320
321+ this . logger . info ( 'Subscribed to MLS NEW_EPOCH events for subconversation' , { subconversationGroupId} ) ;
262322 return ( ) => this . mlsService . off ( MLSServiceEvents . NEW_EPOCH , forwardNewEpoch ) ;
263323 }
264324
265325 public async removeClientFromConferenceSubconversation (
266326 conversationId : QualifiedId ,
267327 clientToRemove : { user : QualifiedId ; clientId : string } ,
268328 ) : Promise < void > {
329+ this . logger . info ( 'Removing client from conference subconversation' , {
330+ conversationId,
331+ user : clientToRemove . user ,
332+ clientId : clientToRemove . clientId ,
333+ } ) ;
269334 const subconversationGroupId = await this . getSubconversationGroupId ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
270335
271336 if ( ! subconversationGroupId ) {
337+ this . logger . warn ( 'Cannot remove client: subconversation groupId missing' , { conversationId} ) ;
272338 return ;
273339 }
274340
275341 const doesMLSGroupExist = await this . mlsService . conversationExists ( subconversationGroupId ) ;
276342 if ( ! doesMLSGroupExist ) {
343+ this . logger . debug ( 'Cannot remove client: subconversation MLS group does not exist locally' , {
344+ conversationId,
345+ subconversationGroupId,
346+ } ) ;
277347 return ;
278348 }
279349
@@ -291,37 +361,84 @@ export class SubconversationService extends TypedEventEmitter<Events> {
291361 ) ;
292362
293363 if ( ! isSubconversationMember ) {
364+ this . logger . info ( 'Client is not a member of the subconversation; nothing to remove' , {
365+ conversationId,
366+ subconversationGroupId,
367+ clientToRemoveQualifiedId,
368+ } ) ;
294369 return ;
295370 }
296371
297- return void this . mlsService . removeClientsFromConversation ( subconversationGroupId , [ clientToRemoveQualifiedId ] ) ;
372+ this . logger . info ( 'Removing client from subconversation' , {
373+ subconversationGroupId,
374+ clientToRemoveQualifiedId,
375+ } ) ;
376+ try {
377+ await this . mlsService . removeClientsFromConversation ( subconversationGroupId , [ clientToRemoveQualifiedId ] ) ;
378+ } catch ( error ) {
379+ this . logger . error ( 'Failed to remove client from subconversation' , {
380+ subconversationGroupId,
381+ clientToRemoveQualifiedId,
382+ error,
383+ } ) ;
384+ }
298385 }
299386
300387 private async joinSubconversationByExternalCommit ( conversationId : QualifiedId , subconversation : SUBCONVERSATION_ID ) {
301- await this . mlsService . joinByExternalCommit ( ( ) =>
302- this . apiClient . api . conversation . getSubconversationGroupInfo ( conversationId , subconversation ) ,
303- ) ;
388+ try {
389+ this . logger . info ( 'Joining subconversation by external commit' , { conversationId, subconversation} ) ;
390+ await this . mlsService . joinByExternalCommit ( ( ) =>
391+ this . apiClient . api . conversation . getSubconversationGroupInfo ( conversationId , subconversation ) ,
392+ ) ;
393+ } catch ( error ) {
394+ this . logger . error ( 'Failed to join subconversation by external commit' , {
395+ conversationId,
396+ subconversation,
397+ error,
398+ } ) ;
399+ throw error ;
400+ }
304401 }
305402
306403 private async getConferenceSubconversation ( conversationId : QualifiedId ) : Promise < Subconversation > {
307- return this . apiClient . api . conversation . getSubconversation ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
404+ this . logger . debug ( 'Fetching conference subconversation metadata' , { conversationId} ) ;
405+ try {
406+ return await this . apiClient . api . conversation . getSubconversation ( conversationId , SUBCONVERSATION_ID . CONFERENCE ) ;
407+ } catch ( error ) {
408+ this . logger . error ( 'Failed to fetch conference subconversation metadata' , { conversationId, error} ) ;
409+ throw error ;
410+ }
308411 }
309412
310413 private async deleteConferenceSubconversation (
311414 conversationId : QualifiedId ,
312415 data : { groupId : string ; epoch : number } ,
313416 ) : Promise < void > {
314- return this . apiClient . api . conversation . deleteSubconversation ( conversationId , SUBCONVERSATION_ID . CONFERENCE , data ) ;
417+ this . logger . info ( 'Deleting conference subconversation' , { conversationId, data} ) ;
418+ try {
419+ return await this . apiClient . api . conversation . deleteSubconversation (
420+ conversationId ,
421+ SUBCONVERSATION_ID . CONFERENCE ,
422+ data ,
423+ ) ;
424+ } catch ( error ) {
425+ this . logger . error ( 'Failed to delete conference subconversation' , { conversationId, data, error} ) ;
426+ throw error ;
427+ }
315428 }
316429
317430 private async generateSubconversationMembers (
318431 subconversationGroupId : string ,
319432 parentGroupId : string ,
320433 ) : Promise < SubconversationEpochInfoMember [ ] > {
434+ this . logger . debug ( 'Generating subconversation members info' , {
435+ subconversationGroupId,
436+ parentGroupId,
437+ } ) ;
321438 const subconversationMemberIds = await this . mlsService . getClientIds ( subconversationGroupId ) ;
322439 const parentMemberIds = await this . mlsService . getClientIds ( parentGroupId ) ;
323440
324- return parentMemberIds . map ( parentMember => {
441+ const members = parentMemberIds . map ( parentMember => {
325442 const isSubconversationMember = subconversationMemberIds . some (
326443 ( { userId, clientId, domain} ) =>
327444 constructFullyQualifiedClientId ( userId , clientId , domain ) ===
@@ -336,6 +453,12 @@ export class SubconversationService extends TypedEventEmitter<Events> {
336453 in_subconv : isSubconversationMember ,
337454 } ;
338455 } ) ;
456+ this . logger . debug ( 'Generated subconversation members info' , {
457+ subconversationGroupId,
458+ parentGroupId,
459+ membersCount : members . length ,
460+ } ) ;
461+ return members ;
339462 }
340463
341464 public getSubconversationGroupId = async (
@@ -346,7 +469,11 @@ export class SubconversationService extends TypedEventEmitter<Events> {
346469 'subconversations' ,
347470 generateSubconversationStoreKey ( parentConversationId , subconversationId ) ,
348471 ) ;
349-
472+ this . logger . debug ( 'Loaded subconversation groupId from store' , {
473+ parentConversationId,
474+ subconversationId,
475+ found : Boolean ( foundSubconversation ?. groupId ) ,
476+ } ) ;
350477 return foundSubconversation ?. groupId ;
351478 } ;
352479
@@ -359,11 +486,15 @@ export class SubconversationService extends TypedEventEmitter<Events> {
359486 groupId : string ;
360487 } [ ]
361488 > => {
489+ this . logger . debug ( 'Retrieving all subconversations by subconversationId' , { subconversationId} ) ;
362490 const allSubconversations = await this . coreDatabase . getAll ( 'subconversations' ) ;
363491 const foundSubconversations = allSubconversations . filter (
364492 subconversation => subconversation . subconversationId === subconversationId ,
365493 ) ;
366-
494+ this . logger . debug ( 'Found subconversations by id' , {
495+ subconversationId,
496+ count : foundSubconversations . length ,
497+ } ) ;
367498 return foundSubconversations ;
368499 } ;
369500
@@ -372,6 +503,11 @@ export class SubconversationService extends TypedEventEmitter<Events> {
372503 subconversationId : SUBCONVERSATION_ID ,
373504 groupId : string ,
374505 ) => {
506+ this . logger . debug ( 'Saving subconversation groupId mapping' , {
507+ parentConversationId,
508+ subconversationId,
509+ groupId,
510+ } ) ;
375511 return this . coreDatabase . put (
376512 'subconversations' ,
377513 { parentConversationId, subconversationId, groupId} ,
@@ -383,6 +519,10 @@ export class SubconversationService extends TypedEventEmitter<Events> {
383519 parentConversationId : QualifiedId ,
384520 subconversationId : SUBCONVERSATION_ID ,
385521 ) => {
522+ this . logger . debug ( 'Clearing subconversation groupId mapping' , {
523+ parentConversationId,
524+ subconversationId,
525+ } ) ;
386526 return this . coreDatabase . delete (
387527 'subconversations' ,
388528 generateSubconversationStoreKey ( parentConversationId , subconversationId ) ,
0 commit comments