amazon-connect / amazon-connect-chatjs

Amazon Connect ChatJS - a browser-based contact center integration API for Chat on the Agent and Customer side.
https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html
Apache License 2.0
93 stars 54 forks source link

Fixing delivered receipt logic to avoid deadlock promise #185

Closed mliao95 closed 1 year ago

mliao95 commented 1 year ago

Issue #, if available: Fixes #130 #131

Description of changes: See delivered receipt handling logic:

if(eventType === CHAT_EVENTS.INCOMING_READ_RECEIPT) { 
         var sendEventPromise = callback.call(ChatClientContext, ...args); 
         self.resolveReadPromises(messageId, sendEventPromise); 
         self.logger.debug('send Read event:', callback, args); 
     } else { 
         //delivered event is the last event fired 
         //fire delivered for latest messageId 
         //fire read for latest messageId 
         var PromiseArr = [callback.call(ChatClientContext, ...args)]; 
         if(this.lastReadArgs) { 
             var contentVal = typeof this.lastReadArgs[2] === "string" ? JSON.parse(this.lastReadArgs[2]) : this.lastReadArgs[2]; 
             var readEventMessageId = contentVal.messageId; 
             // if readPromise has been resolved for readEventMessageId; readPromiseMap should not contain readEventMessageId 
             // if readPromiseMap contains readEventMessageId; read event has not been called! 
             if (self.readPromiseMap.has(readEventMessageId)) { 
                 PromiseArr.push(callback.call(ChatClientContext, ...this.lastReadArgs)); 
             } 
         } 
         self.logger.debug('send Delivered event:', args, 'read event:', this.lastReadArgs); 
         Promise.all(PromiseArr).then(res => { 
             self.resolveReadPromises(contentVal.messageId, res[0]); 
             self.resolveDeliveredPromises(messageId, res[0]); 
         }); 
     } 

Delivered receipt logic is a bit flawed since it assumes two things:

The proposed fix will run each promise regardless of how the other promise resolves (using Promise.allSettled) and will also only call resolveReadPromises if there is a read receipt promise (indicated by res array size).

Promise.allSettled(PromiseArr).then(res => {
                        self.resolveDeliveredPromises(messageId, res[0].value || res[0].reason, res[0].status === "rejected");
                        if (res.length > 1) {
                            self.resolveReadPromises(contentVal.messageId, res[1].value || res[1].reason, res[1].status === "rejected");
                        }
                    });