@@ -515,21 +515,26 @@ export class ChatEntitlementRequests extends Disposable {
515
515
}
516
516
}
517
517
518
- private async findMatchingProviderSession ( token : CancellationToken ) : Promise < AuthenticationSession | undefined > {
518
+ private async findMatchingProviderSession ( token : CancellationToken ) : Promise < AuthenticationSession [ ] | undefined > {
519
519
const sessions = await this . doGetSessions ( ChatEntitlementRequests . providerId ( this . configurationService ) ) ;
520
520
if ( token . isCancellationRequested ) {
521
521
return undefined ;
522
522
}
523
523
524
+ const matchingSessions = new Set < AuthenticationSession > ( ) ;
524
525
for ( const session of sessions ) {
525
526
for ( const scopes of defaultChat . providerScopes ) {
526
- if ( this . scopesMatch ( session . scopes , scopes ) ) {
527
- return session ;
527
+ if ( this . includesScopes ( session . scopes , scopes ) ) {
528
+ matchingSessions . add ( session ) ;
528
529
}
529
530
}
530
531
}
531
532
532
- return undefined ;
533
+ // We intentionally want to return an array of matching sessions and
534
+ // not just the first, because it is possible that a matching session
535
+ // has an expired token. As such, we want to try them all until we
536
+ // succeeded with the request.
537
+ return matchingSessions . size > 0 ? Array . from ( matchingSessions ) : undefined ;
533
538
}
534
539
535
540
private async doGetSessions ( providerId : string ) : Promise < readonly AuthenticationSession [ ] > {
@@ -551,12 +556,12 @@ export class ChatEntitlementRequests extends Disposable {
551
556
return [ ] ;
552
557
}
553
558
554
- private scopesMatch ( scopes : ReadonlyArray < string > , expectedScopes : string [ ] ) : boolean {
555
- return scopes . length === expectedScopes . length && expectedScopes . every ( scope => scopes . includes ( scope ) ) ;
559
+ private includesScopes ( scopes : ReadonlyArray < string > , expectedScopes : string [ ] ) : boolean {
560
+ return expectedScopes . every ( scope => scopes . includes ( scope ) ) ;
556
561
}
557
562
558
- private async resolveEntitlement ( session : AuthenticationSession , token : CancellationToken ) : Promise < IEntitlements | undefined > {
559
- const entitlements = await this . doResolveEntitlement ( session , token ) ;
563
+ private async resolveEntitlement ( sessions : AuthenticationSession [ ] , token : CancellationToken ) : Promise < IEntitlements | undefined > {
564
+ const entitlements = await this . doResolveEntitlement ( sessions , token ) ;
560
565
if ( typeof entitlements ?. entitlement === 'number' && ! token . isCancellationRequested ) {
561
566
this . didResolveEntitlements = true ;
562
567
this . update ( entitlements ) ;
@@ -565,7 +570,7 @@ export class ChatEntitlementRequests extends Disposable {
565
570
return entitlements ;
566
571
}
567
572
568
- private async doResolveEntitlement ( session : AuthenticationSession , token : CancellationToken ) : Promise < IEntitlements | undefined > {
573
+ private async doResolveEntitlement ( sessions : AuthenticationSession [ ] , token : CancellationToken ) : Promise < IEntitlements | undefined > {
569
574
if ( ChatEntitlementRequests . providerId ( this . configurationService ) === defaultChat . provider . enterprise . id ) {
570
575
this . logService . trace ( '[chat entitlement]: enterprise provider, assuming Enterprise plan' ) ;
571
576
return { entitlement : ChatEntitlement . Enterprise } ;
@@ -575,7 +580,7 @@ export class ChatEntitlementRequests extends Disposable {
575
580
return undefined ;
576
581
}
577
582
578
- const response = await this . request ( defaultChat . entitlementUrl , 'GET' , undefined , session , token ) ;
583
+ const response = await this . request ( defaultChat . entitlementUrl , 'GET' , undefined , sessions , token ) ;
579
584
if ( token . isCancellationRequested ) {
580
585
return undefined ;
581
586
}
@@ -713,26 +718,42 @@ export class ChatEntitlementRequests extends Disposable {
713
718
return quotas ;
714
719
}
715
720
716
- private async request ( url : string , type : 'GET' , body : undefined , session : AuthenticationSession , token : CancellationToken ) : Promise < IRequestContext | undefined > ;
717
- private async request ( url : string , type : 'POST' , body : object , session : AuthenticationSession , token : CancellationToken ) : Promise < IRequestContext | undefined > ;
718
- private async request ( url : string , type : 'GET' | 'POST' , body : object | undefined , session : AuthenticationSession , token : CancellationToken ) : Promise < IRequestContext | undefined > {
719
- try {
720
- return await this . requestService . request ( {
721
- type,
722
- url,
723
- data : type === 'POST' ? JSON . stringify ( body ) : undefined ,
724
- disableCache : true ,
725
- headers : {
726
- 'Authorization' : `Bearer ${ session . accessToken } `
727
- }
728
- } , token ) ;
729
- } catch ( error ) {
730
- if ( ! token . isCancellationRequested ) {
731
- this . logService . error ( `[chat entitlement] request: error ${ error } ` ) ;
721
+ private async request ( url : string , type : 'GET' , body : undefined , sessions : AuthenticationSession [ ] , token : CancellationToken ) : Promise < IRequestContext | undefined > ;
722
+ private async request ( url : string , type : 'POST' , body : object , sessions : AuthenticationSession [ ] , token : CancellationToken ) : Promise < IRequestContext | undefined > ;
723
+ private async request ( url : string , type : 'GET' | 'POST' , body : object | undefined , sessions : AuthenticationSession [ ] , token : CancellationToken ) : Promise < IRequestContext | undefined > {
724
+ let lastRequest : IRequestContext | undefined ;
725
+
726
+ for ( const session of sessions ) {
727
+ if ( token . isCancellationRequested ) {
728
+ return lastRequest ;
732
729
}
733
730
734
- return undefined ;
731
+ try {
732
+ const response = await this . requestService . request ( {
733
+ type,
734
+ url,
735
+ data : type === 'POST' ? JSON . stringify ( body ) : undefined ,
736
+ disableCache : true ,
737
+ headers : {
738
+ 'Authorization' : `Bearer ${ session . accessToken } `
739
+ }
740
+ } , token ) ;
741
+
742
+ const status = response . res . statusCode ;
743
+ if ( status && status !== 200 ) {
744
+ lastRequest = response ;
745
+ continue ; // try next session
746
+ }
747
+
748
+ return response ;
749
+ } catch ( error ) {
750
+ if ( ! token . isCancellationRequested ) {
751
+ this . logService . error ( `[chat entitlement] request: error ${ error } ` ) ;
752
+ }
753
+ }
735
754
}
755
+
756
+ return lastRequest ;
736
757
}
737
758
738
759
private update ( state : IEntitlements ) : void {
@@ -745,28 +766,28 @@ export class ChatEntitlementRequests extends Disposable {
745
766
}
746
767
}
747
768
748
- async forceResolveEntitlement ( session : AuthenticationSession | undefined , token = CancellationToken . None ) : Promise < IEntitlements | undefined > {
749
- if ( ! session ) {
750
- session = await this . findMatchingProviderSession ( token ) ;
769
+ async forceResolveEntitlement ( sessions : AuthenticationSession [ ] | undefined , token = CancellationToken . None ) : Promise < IEntitlements | undefined > {
770
+ if ( ! sessions ) {
771
+ sessions = await this . findMatchingProviderSession ( token ) ;
751
772
}
752
773
753
- if ( ! session ) {
774
+ if ( ! sessions || sessions . length === 0 ) {
754
775
return undefined ;
755
776
}
756
777
757
- return this . resolveEntitlement ( session , token ) ;
778
+ return this . resolveEntitlement ( sessions , token ) ;
758
779
}
759
780
760
- async signUpFree ( session : AuthenticationSession ) : Promise < true /* signed up */ | false /* already signed up */ | { errorCode : number } /* error */ > {
781
+ async signUpFree ( sessions : AuthenticationSession [ ] ) : Promise < true /* signed up */ | false /* already signed up */ | { errorCode : number } /* error */ > {
761
782
const body = {
762
783
restricted_telemetry : this . telemetryService . telemetryLevel === TelemetryLevel . NONE ? 'disabled' : 'enabled' ,
763
784
public_code_suggestions : 'enabled'
764
785
} ;
765
786
766
- const response = await this . request ( defaultChat . entitlementSignupLimitedUrl , 'POST' , body , session , CancellationToken . None ) ;
787
+ const response = await this . request ( defaultChat . entitlementSignupLimitedUrl , 'POST' , body , sessions , CancellationToken . None ) ;
767
788
if ( ! response ) {
768
789
const retry = await this . onUnknownSignUpError ( localize ( 'signUpNoResponseError' , "No response received." ) , '[chat entitlement] sign-up: no response' ) ;
769
- return retry ? this . signUpFree ( session ) : { errorCode : 1 } ;
790
+ return retry ? this . signUpFree ( sessions ) : { errorCode : 1 } ;
770
791
}
771
792
772
793
if ( response . res . statusCode && response . res . statusCode !== 200 ) {
@@ -785,7 +806,7 @@ export class ChatEntitlementRequests extends Disposable {
785
806
}
786
807
}
787
808
const retry = await this . onUnknownSignUpError ( localize ( 'signUpUnexpectedStatusError' , "Unexpected status code {0}." , response . res . statusCode ) , `[chat entitlement] sign-up: unexpected status code ${ response . res . statusCode } ` ) ;
788
- return retry ? this . signUpFree ( session ) : { errorCode : response . res . statusCode } ;
809
+ return retry ? this . signUpFree ( sessions ) : { errorCode : response . res . statusCode } ;
789
810
}
790
811
791
812
let responseText : string | null = null ;
@@ -797,7 +818,7 @@ export class ChatEntitlementRequests extends Disposable {
797
818
798
819
if ( ! responseText ) {
799
820
const retry = await this . onUnknownSignUpError ( localize ( 'signUpNoResponseContentsError' , "Response has no contents." ) , '[chat entitlement] sign-up: response has no content' ) ;
800
- return retry ? this . signUpFree ( session ) : { errorCode : 2 } ;
821
+ return retry ? this . signUpFree ( sessions ) : { errorCode : 2 } ;
801
822
}
802
823
803
824
let parsedResult : { subscribed : boolean } | undefined = undefined ;
@@ -806,7 +827,7 @@ export class ChatEntitlementRequests extends Disposable {
806
827
this . logService . trace ( `[chat entitlement] sign-up: response is ${ responseText } ` ) ;
807
828
} catch ( err ) {
808
829
const retry = await this . onUnknownSignUpError ( localize ( 'signUpInvalidResponseError' , "Invalid response contents." ) , `[chat entitlement] sign-up: error parsing response (${ err } )` ) ;
809
- return retry ? this . signUpFree ( session ) : { errorCode : 3 } ;
830
+ return retry ? this . signUpFree ( sessions ) : { errorCode : 3 } ;
810
831
}
811
832
812
833
// We have made it this far, so the user either did sign-up or was signed-up already.
@@ -862,7 +883,7 @@ export class ChatEntitlementRequests extends Disposable {
862
883
this . authenticationExtensionsService . updateAccountPreference ( defaultChat . extensionId , providerId , session . account ) ;
863
884
this . authenticationExtensionsService . updateAccountPreference ( defaultChat . chatExtensionId , providerId , session . account ) ;
864
885
865
- const entitlements = await this . forceResolveEntitlement ( session ) ;
886
+ const entitlements = await this . forceResolveEntitlement ( [ session ] ) ;
866
887
867
888
return { session, entitlements } ;
868
889
}
0 commit comments