@@ -279,7 +279,7 @@ const errorFailedToConnect = new Error("failed to connect");
279279
280280const konsole = {
281281 log : ( message ?: any , ... optionalParams : any [ ] ) => {
282- if ( typeof ( window ) !== 'undefined' && ( window as any ) [ "botchatDebug" ] && message )
282+ if ( typeof window !== 'undefined' && ( window as any ) [ "botchatDebug" ] && message )
283283 console . log ( message , ... optionalParams ) ;
284284 }
285285}
@@ -319,23 +319,23 @@ export class DirectLine implements IBotConnection {
319319 if ( options . domain ) {
320320 this . domain = options . domain ;
321321 }
322-
322+
323323 if ( options . conversationId ) {
324324 this . conversationId = options . conversationId ;
325325 }
326-
326+
327327 if ( options . watermark ) {
328328 this . watermark = options . watermark ;
329329 }
330-
330+
331331 if ( options . streamUrl ) {
332332 if ( options . token && options . conversationId ) {
333333 this . streamUrl = options . streamUrl ;
334334 } else {
335335 console . warn ( 'streamUrl was ignored: you need to provide a token and a conversationid' ) ;
336336 }
337337 }
338-
338+
339339 if ( options . pollingInterval !== undefined ) {
340340 this . pollingInterval = options . pollingInterval ;
341341 }
@@ -461,7 +461,11 @@ export class DirectLine implements IBotConnection {
461461 // if the token is expired there's no reason to keep trying
462462 this . expiredToken ( ) ;
463463 return Observable . throw ( error ) ;
464+ } else if ( error . status === 404 ) {
465+ // If the bot is gone, we should stop retrying
466+ return Observable . throw ( error ) ;
464467 }
468+
465469 return Observable . of ( error ) ;
466470 } )
467471 . delay ( timeout )
@@ -600,37 +604,55 @@ export class DirectLine implements IBotConnection {
600604 }
601605
602606 private pollingGetActivity$ ( ) {
603- return Observable . interval ( this . pollingInterval )
604- . combineLatest ( this . checkConnection ( ) )
605- . flatMap ( ( [ _ , connectionStatus ] ) => {
606- if ( connectionStatus !== ConnectionStatus . Online )
607- return Observable . empty < Activity > ( )
608-
609- return Observable . ajax ( {
610- method : "GET" ,
611- url : `${ this . domain } /conversations/${ this . conversationId } /activities?watermark=${ this . watermark } ` ,
612- timeout,
613- headers : {
614- "Accept" : "application/json" ,
615- "Authorization" : `Bearer ${ this . token } `
616- }
617- } )
618- . catch ( error => {
619- if ( error . status === 403 ) {
620- // This is slightly ugly. We want to update this.connectionStatus$ to ExpiredToken so that subsequent
621- // calls to checkConnection will throw an error. But when we do so, it causes this.checkConnection()
622- // to immediately throw an error, which is caught by the catch() below and transformed into an empty
623- // object. Then next() returns, and we emit an empty object. Which means one 403 is causing
624- // two empty objects to be emitted. Which is harmless but, again, slightly ugly.
625- this . expiredToken ( ) ;
607+ const poller$ : Observable < AjaxResponse > = Observable . create ( ( subscriber : Subscriber < any > ) => {
608+ // A BehaviorSubject to trigger polling. Since it is a BehaviorSubject
609+ // the first event is produced immediately.
610+ const trigger$ = new BehaviorSubject < any > ( { } ) ;
611+
612+ trigger$ . subscribe ( ( ) => {
613+ if ( this . connectionStatus$ . getValue ( ) === ConnectionStatus . Online ) {
614+ const startTimestamp = Date . now ( ) ;
615+
616+ Observable . ajax ( {
617+ headers : {
618+ Accept : 'application/json' ,
619+ Authorization : `Bearer ${ this . token } `
620+ } ,
621+ method : 'GET' ,
622+ url : `${ this . domain } /conversations/${ this . conversationId } /activities?watermark=${ this . watermark } ` ,
623+ timeout
624+ } ) . subscribe (
625+ ( result : AjaxResponse ) => {
626+ subscriber . next ( result ) ;
627+ setTimeout ( ( ) => trigger$ . next ( null ) , Math . max ( 0 , this . pollingInterval - Date . now ( ) + startTimestamp ) ) ;
628+ } ,
629+ ( error : any ) => {
630+ switch ( error . status ) {
631+ case 403 :
632+ this . connectionStatus$ . next ( ConnectionStatus . ExpiredToken ) ;
633+ setTimeout ( ( ) => trigger$ . next ( null ) , this . pollingInterval ) ;
634+ break ;
635+
636+ case 404 :
637+ this . connectionStatus$ . next ( ConnectionStatus . Ended ) ;
638+ break ;
639+
640+ default :
641+ // propagate the error
642+ subscriber . error ( error ) ;
643+ break ;
644+ }
645+ }
646+ ) ;
626647 }
627- return Observable . empty < AjaxResponse > ( ) ;
628- } )
629- // .do(ajaxResponse => konsole.log("getActivityGroup ajaxResponse", ajaxResponse))
648+ } ) ;
649+ } ) ;
650+
651+ return this . checkConnection ( )
652+ . flatMap ( _ => poller$
653+ . catch ( ( ) => Observable . empty < AjaxResponse > ( ) )
630654 . map ( ajaxResponse => ajaxResponse . response as ActivityGroup )
631- . flatMap ( activityGroup => this . observableFromActivityGroup ( activityGroup ) )
632- } )
633- . catch ( error => Observable . empty < Activity > ( ) ) ;
655+ . flatMap ( activityGroup => this . observableFromActivityGroup ( activityGroup ) ) ) ;
634656 }
635657
636658 private observableFromActivityGroup ( activityGroup : ActivityGroup ) {
@@ -711,13 +733,15 @@ export class DirectLine implements IBotConnection {
711733 // token has expired. We can't recover from this here, but the embedding
712734 // website might eventually call reconnect() with a new token and streamUrl.
713735 this . expiredToken ( ) ;
736+ } else if ( error . status === 404 ) {
737+ return Observable . throw ( errorConversationEnded ) ;
714738 }
739+
715740 return Observable . of ( error ) ;
716741 } )
717742 . delay ( timeout )
718743 . take ( retries )
719744 )
720745 )
721746 }
722-
723747}
0 commit comments