Skip to content

Commit

Permalink
refactor: debounce prompts using separate prompt mutexes
Browse files Browse the repository at this point in the history
Signed-off-by: Trae Yelovich <trae.yelovich@broadcom.com>
  • Loading branch information
traeok committed Feb 25, 2025
1 parent 0e2e471 commit 9c43211
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 13 deletions.
23 changes: 11 additions & 12 deletions packages/zowe-explorer-api/src/profiles/AuthHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export type AuthPromptParams = {

export type ProfileLike = string | imperative.IProfileLoaded;
export class AuthHandler {
private static profileLocks: Map<string, Mutex> = new Map();
private static authPromptLocks = new Map<string, Mutex>();
private static profileLocks = new Map<string, Mutex>();
private static enabledProfileTypes: Set<string> = new Set(["zosmf"]);

/**
Expand Down Expand Up @@ -80,6 +81,7 @@ export class AuthHandler {
*/
public static unlockProfile(profile: ProfileLike, refreshResources?: boolean): void {
const profileName = AuthHandler.getProfileName(profile);
this.authPromptLocks.get(profileName)?.release();
const mutex = this.profileLocks.get(profileName);
// If a mutex doesn't exist for this profile or the mutex is no longer locked, return
if (mutex == null || !mutex.isLocked()) {
Expand All @@ -101,26 +103,23 @@ export class AuthHandler {
}
}

private static authErrorTimestamps = new Map<string, number>();
private static readonly DEBOUNCE_WINDOW_MS = 2000; // 2 seconds

/**
* Determines whether to handle an authentication error for a given profile.
* This debounces authentication errors so we don't spam the user with authentication prompts during parallel requests.
* This uses a mutex to prevent additional authentication prompts until the first prompt is resolved.
* @param profileName The name of the profile to check
* @returns {boolean} Whether to handle the authentication error
*/
public static shouldHandleAuthError(profileName: string): boolean {
const now = Date.now();
const lastErrorTime = this.authErrorTimestamps.get(profileName) || 0;
public static async shouldHandleAuthError(profileName: string): Promise<boolean> {
if (!this.authPromptLocks.has(profileName)) {
this.authPromptLocks.set(profileName, new Mutex());
}

// If we've seen an auth error for this profile within the debounce window, don't handle it again
if (now - lastErrorTime < this.DEBOUNCE_WINDOW_MS) {
const mutex = this.authPromptLocks.get(profileName);
if (mutex.isLocked()) {
return false;
}

// Update the timestamp and allow handling this error
this.authErrorTimestamps.set(profileName, now);
await mutex.acquire();
return true;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/zowe-explorer/src/utils/AuthUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ export class AuthUtils {
imperativeError: err,
isUsingTokenAuth: await AuthUtils.isUsingTokenAuth(profile.name),
errorCorrelation,
useModal: true,
};
// If the profile is already locked, prompt the user to re-authenticate.
if (AuthHandler.isProfileLocked(profile)) {
await AuthHandler.promptForAuthentication(profile, authOpts);
await AuthHandler.waitForUnlock(profile);
} else {
// Lock the profile and prompt the user for authentication by providing login/credential prompt options.
await AuthHandler.lockProfile(profile, authOpts);
Expand Down Expand Up @@ -118,6 +119,7 @@ export class AuthUtils {
imperativeError,
isUsingTokenAuth: await AuthUtils.isUsingTokenAuth(profile.name),
errorCorrelation,
useModal: true,
});
}
}
Expand Down

0 comments on commit 9c43211

Please sign in to comment.