88import { CodeLocation , Fix , Suggestion , Violation } from '../diagnostics' ;
99import { Logger } from "../logger" ;
1010import { getErrorMessage , indent } from '../utils' ;
11- import { HttpMethods , HttpRequest , OrgConnectionService } from '../external-services/org-connection-service' ;
11+ import { HttpMethods , HttpRequest , OrgConnectionService , OrgUserInfo } from '../external-services/org-connection-service' ;
1212import { FileHandler } from '../fs-utils' ;
1313import { messages } from '../messages' ;
14+ import { EventEmitter } from 'node:stream' ;
1415
1516export const APEX_GURU_ENGINE_NAME : string = 'apexguru' ;
1617const APEX_GURU_MAX_TIMEOUT_SECONDS = 60 ;
@@ -24,7 +25,9 @@ const RESPONSE_STATUS = {
2425}
2526
2627export interface ApexGuruService {
27- getAvailability ( ) : Promise < ApexGuruAvailability > ;
28+ getAvailability ( ) : ApexGuruAvailability ;
29+ updateAvailability ( ) : Promise < void > ;
30+ onAccessChange ( callback : ( access : ApexGuruAccess ) => void ) : void ;
2831 scan ( absFileToScan : string ) : Promise < Violation [ ] > ;
2932}
3033
@@ -48,12 +51,15 @@ export enum ApexGuruAccess {
4851 NOT_AUTHED = "not-authed"
4952}
5053
54+ const ACCESS_CHANGED_EVENT = "apexGuruAccessChanged" ;
55+
5156export class LiveApexGuruService implements ApexGuruService {
5257 private readonly orgConnectionService : OrgConnectionService ;
5358 private readonly fileHandler : FileHandler ;
5459 private readonly logger : Logger ;
5560 private readonly maxTimeoutSeconds : number ;
5661 private readonly retryIntervalMillis : number ;
62+ private readonly eventEmitter : EventEmitter = new EventEmitter ( ) ;
5763 private availability ?: ApexGuruAvailability ;
5864
5965 constructor (
@@ -67,42 +73,60 @@ export class LiveApexGuruService implements ApexGuruService {
6773 this . logger = logger ;
6874 this . maxTimeoutSeconds = maxTimeoutSeconds ;
6975 this . retryIntervalMillis = retryIntervalMillis ;
76+
77+ // Every time an org is changed (authed or unauthed) then we recalculate the availability asyncronously
78+ orgConnectionService . onOrgChange ( ( _orgUserInfo : OrgUserInfo ) => {
79+ void this . updateAvailability ( ) ;
80+ } ) ;
7081 }
7182
72- async getAvailability ( ) : Promise < ApexGuruAvailability > {
83+ getAvailability ( ) : ApexGuruAvailability {
7384 if ( this . availability === undefined ) {
74- await this . updateAvailability ( ) ;
85+ // This should never happen in production because updateAvailability must be called prior to enabling
86+ // the user to even have access to any of the ApexGuru scan buttons. If it does, we should investigate.
87+ throw new Error ( 'The getAvailability method should not be called until updateAvailability is first called' ) ;
7588 }
7689 return this . availability ;
7790 }
7891
79- // TODO: Soon with W-18538308 we will be using the connection.onOrgChange to wire up to this
80- private async updateAvailability ( ) : Promise < void > {
92+ onAccessChange ( callback : ( access : ApexGuruAccess ) => void ) : void {
93+ this . eventEmitter . addListener ( ACCESS_CHANGED_EVENT , callback ) ;
94+ }
95+
96+ async updateAvailability ( ) : Promise < void > {
8197 if ( ! this . orgConnectionService . isAuthed ( ) ) {
82- this . availability = {
98+ this . setAvailability ( {
8399 access : ApexGuruAccess . NOT_AUTHED ,
84100 message : messages . apexGuru . noOrgAuthed
85- } ;
101+ } ) ;
86102 return ;
87103 }
88104
89105 const response : ApexGuruResponse = await this . request ( 'GET' , await this . getValidateEndpoint ( ) ) ;
90106
91107 if ( response . status === RESPONSE_STATUS . SUCCESS ) {
92- this . availability = {
108+ this . setAvailability ( {
93109 access : ApexGuruAccess . ENABLED ,
94110
95111 // This message isn't used anywhere except for debugging purposes and it allows us to make message field
96112 // a string instead of a string | undefined.
97113 message : "ApexGuru access is enabled."
98- } ;
114+ } ) ;
99115 } else {
100- this . availability = {
116+ this . setAvailability ( {
101117 access : response . status === RESPONSE_STATUS . FAILED ? ApexGuruAccess . ELIGIBLE : ApexGuruAccess . INELIGIBLE ,
102118
103119 // There should always be a message on failed and error responses, but adding this here just in case
104120 message : response . message ?? `ApexGuru access is not enabled. Response: ${ JSON . stringify ( response ) } `
105- } ;
121+ } ) ;
122+ }
123+ }
124+
125+ private setAvailability ( availability : ApexGuruAvailability ) {
126+ const oldAccess : ApexGuruAccess | undefined = this . availability ?. access ;
127+ this . availability = availability ;
128+ if ( availability . access !== oldAccess ) {
129+ this . eventEmitter . emit ( ACCESS_CHANGED_EVENT , availability . access ) ;
106130 }
107131 }
108132
0 commit comments