From 61ca374748dae56193605ffd3e8feeed13a15d7f Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Mon, 8 Jul 2024 14:08:16 +0300 Subject: [PATCH 1/2] feat: added timeout before next retry for 429 error --- .../inbox-list-threads-module.ts | 9 +++++-- .../common/api/email-provider/gmail/gmail.ts | 25 +++++++++++++------ extension/js/common/api/shared/api-error.ts | 2 ++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts b/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts index bea9023fcc8..bd60793af19 100644 --- a/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts +++ b/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts @@ -2,7 +2,7 @@ 'use strict'; -import { ApiErr } from '../../../../js/common/api/shared/api-error.js'; +import { ApiErr, MAX_RATE_LIMIT_ERROR_RETRY_COUNT } from '../../../../js/common/api/shared/api-error.js'; import { Catch } from '../../../../js/common/platform/catch.js'; import { GmailParser } from '../../../../js/common/api/email-provider/gmail/gmail-parser.js'; import { InboxView } from '../inbox.js'; @@ -11,6 +11,7 @@ import { Str, promiseAllWithLimit } from '../../../../js/common/core/common.js'; import { Ui } from '../../../../js/common/browser/ui.js'; import { ViewModule } from '../../../../js/common/view-module.js'; import { Xss } from '../../../../js/common/platform/xss.js'; +import { Time } from '../../../../js/common/browser/time.js'; export class InboxListThreadsModule extends ViewModule { public render = async (labelId: string) => { @@ -42,7 +43,7 @@ export class InboxListThreadsModule extends ViewModule { } }; - private renderInboxItem = async (threadId: string): Promise => { + private renderInboxItem = async (threadId: string, retryCount = 0): Promise => { this.inboxThreadItemAdd(threadId); const threadItem = $('.threads #' + this.threadListItemId(threadId)); try { @@ -73,6 +74,10 @@ export class InboxListThreadsModule extends ViewModule { threadItem.find('.msg_count').text(`(${thread.messages.length})`); } } catch (e) { + if (ApiErr.isRateLimit(e) && retryCount < MAX_RATE_LIMIT_ERROR_RETRY_COUNT) { + await Time.sleep(1000); + return await this.renderInboxItem(threadId, retryCount + 1); + } if (ApiErr.isNetErr(e)) { Xss.sanitizeRender(threadItem.find('.loading'), 'Failed to load (network) retry') .find('a') diff --git a/extension/js/common/api/email-provider/gmail/gmail.ts b/extension/js/common/api/email-provider/gmail/gmail.ts index 42b1b4929b9..3f3b27f440c 100644 --- a/extension/js/common/api/email-provider/gmail/gmail.ts +++ b/extension/js/common/api/email-provider/gmail/gmail.ts @@ -17,7 +17,8 @@ import { Google } from './google.js'; import { GoogleOAuth } from '../../authentication/google/google-oauth.js'; import { SendableMsg } from '../sendable-msg.js'; import { KeyStore } from '../../../platform/store/key-store.js'; -import { AjaxErr } from '../../shared/api-error.js'; +import { AjaxErr, ApiErr, MAX_RATE_LIMIT_ERROR_RETRY_COUNT } from '../../shared/api-error.js'; +import { Time } from '../../../browser/time.js'; export type GmailResponseFormat = 'raw' | 'full' | 'metadata'; @@ -126,13 +127,21 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface { * because strings over 1 MB may fail to get to/from bg page. A way to mitigate that would be to pass `R.GmailMsg$raw` prop * as a Buf instead of a string. */ - public msgGet = async (msgId: string, format: GmailResponseFormat, progressCb?: ProgressCb): Promise => { - return await Google.gmailCall( - this.acctEmail, - `messages/${msgId}`, - { method: 'GET', data: { format: format || 'full' } }, - progressCb ? { download: progressCb } : undefined - ); + public msgGet = async (msgId: string, format: GmailResponseFormat, progressCb?: ProgressCb, retryCount = 0): Promise => { + try { + return await Google.gmailCall( + this.acctEmail, + `messages/${msgId}`, + { method: 'GET', data: { format: format || 'full' } }, + progressCb ? { download: progressCb } : undefined + ); + } catch (e) { + if (ApiErr.isRateLimit(e) && retryCount < MAX_RATE_LIMIT_ERROR_RETRY_COUNT) { + await Time.sleep(1000); + return await this.msgGet(msgId, format, progressCb, retryCount + 1); + } + throw e; + } }; public msgsGet = async (msgIds: string[], format: GmailResponseFormat): Promise => { diff --git a/extension/js/common/api/shared/api-error.ts b/extension/js/common/api/shared/api-error.ts index 01a9319377b..8d2b70c94c7 100644 --- a/extension/js/common/api/shared/api-error.ts +++ b/extension/js/common/api/shared/api-error.ts @@ -29,6 +29,8 @@ abstract class AuthErr extends Error {} export class GoogleAuthErr extends AuthErr {} export class BackendAuthErr extends AuthErr {} +export const MAX_RATE_LIMIT_ERROR_RETRY_COUNT = 5; + abstract class ApiCallErr extends Error { protected static describeApiAction = (req: { url: string; method?: string; data?: unknown }) => { const describeBody = typeof req.data === 'undefined' ? '(no body)' : typeof req.data; From ab9a1b615fe648f3d288a2899be89179e1e31c38 Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Tue, 9 Jul 2024 15:26:26 +0300 Subject: [PATCH 2/2] fix: pr reviews --- .../inbox/inbox-modules/inbox-list-threads-module.ts | 9 ++------- .../js/common/api/email-provider/gmail/gmail.ts | 12 ++++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts b/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts index bd60793af19..bea9023fcc8 100644 --- a/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts +++ b/extension/chrome/settings/inbox/inbox-modules/inbox-list-threads-module.ts @@ -2,7 +2,7 @@ 'use strict'; -import { ApiErr, MAX_RATE_LIMIT_ERROR_RETRY_COUNT } from '../../../../js/common/api/shared/api-error.js'; +import { ApiErr } from '../../../../js/common/api/shared/api-error.js'; import { Catch } from '../../../../js/common/platform/catch.js'; import { GmailParser } from '../../../../js/common/api/email-provider/gmail/gmail-parser.js'; import { InboxView } from '../inbox.js'; @@ -11,7 +11,6 @@ import { Str, promiseAllWithLimit } from '../../../../js/common/core/common.js'; import { Ui } from '../../../../js/common/browser/ui.js'; import { ViewModule } from '../../../../js/common/view-module.js'; import { Xss } from '../../../../js/common/platform/xss.js'; -import { Time } from '../../../../js/common/browser/time.js'; export class InboxListThreadsModule extends ViewModule { public render = async (labelId: string) => { @@ -43,7 +42,7 @@ export class InboxListThreadsModule extends ViewModule { } }; - private renderInboxItem = async (threadId: string, retryCount = 0): Promise => { + private renderInboxItem = async (threadId: string): Promise => { this.inboxThreadItemAdd(threadId); const threadItem = $('.threads #' + this.threadListItemId(threadId)); try { @@ -74,10 +73,6 @@ export class InboxListThreadsModule extends ViewModule { threadItem.find('.msg_count').text(`(${thread.messages.length})`); } } catch (e) { - if (ApiErr.isRateLimit(e) && retryCount < MAX_RATE_LIMIT_ERROR_RETRY_COUNT) { - await Time.sleep(1000); - return await this.renderInboxItem(threadId, retryCount + 1); - } if (ApiErr.isNetErr(e)) { Xss.sanitizeRender(threadItem.find('.loading'), 'Failed to load (network) retry') .find('a') diff --git a/extension/js/common/api/email-provider/gmail/gmail.ts b/extension/js/common/api/email-provider/gmail/gmail.ts index 3f3b27f440c..9a19ff4519f 100644 --- a/extension/js/common/api/email-provider/gmail/gmail.ts +++ b/extension/js/common/api/email-provider/gmail/gmail.ts @@ -34,8 +34,16 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface { } }; - public threadGet = async (threadId: string, format?: GmailResponseFormat, progressCb?: ProgressCb): Promise => { - return await Google.gmailCall(this.acctEmail, `threads/${threadId}`, { method: 'GET', data: { format } }, { download: progressCb }); + public threadGet = async (threadId: string, format?: GmailResponseFormat, progressCb?: ProgressCb, retryCount = 0): Promise => { + try { + return await Google.gmailCall(this.acctEmail, `threads/${threadId}`, { method: 'GET', data: { format } }, { download: progressCb }); + } catch (e) { + if (ApiErr.isRateLimit(e) && retryCount < MAX_RATE_LIMIT_ERROR_RETRY_COUNT) { + await Time.sleep(1000); + return await this.threadGet(threadId, format, progressCb, retryCount + 1); + } + throw e; + } }; public threadList = async (labelId: string): Promise => {