diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2023-06-12 14:31:13 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2023-06-12 14:31:13 +0200 |
| commit | a6193ecb0974af6fc8677208e204b9064a9484dd (patch) | |
| tree | 2147ecef22e855b69aa8569120ad14d82f2061fa /gui/src/main | |
| parent | 601109ac7845bf603e4b2d17bb72f30401706b71 (diff) | |
| parent | 2eecab47023011f96d9f76ce13bfd05e113a6c5b (diff) | |
| download | mullvadvpn-a6193ecb0974af6fc8677208e204b9064a9484dd.tar.xz mullvadvpn-a6193ecb0974af6fc8677208e204b9064a9484dd.zip | |
Merge branch 'too-many-devices-view-isnt-shown-des-186'
Diffstat (limited to 'gui/src/main')
| -rw-r--r-- | gui/src/main/account-data-cache.ts | 47 | ||||
| -rw-r--r-- | gui/src/main/account.ts | 23 | ||||
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 27 | ||||
| -rw-r--r-- | gui/src/main/errors.ts | 37 |
4 files changed, 46 insertions, 88 deletions
diff --git a/gui/src/main/account-data-cache.ts b/gui/src/main/account-data-cache.ts index 38d2cb8a01..a5860f9073 100644 --- a/gui/src/main/account-data-cache.ts +++ b/gui/src/main/account-data-cache.ts @@ -1,13 +1,20 @@ import { closeToExpiry, hasExpired } from '../shared/account-expiry'; -import { AccountToken, IAccountData, VoucherResponse } from '../shared/daemon-rpc-types'; +import { + AccountDataError, + AccountDataResponse, + AccountToken, + IAccountData, + VoucherResponse, +} from '../shared/daemon-rpc-types'; import { dateByAddingComponent, DateComponent } from '../shared/date-helper'; import log from '../shared/logging'; import { Scheduler } from '../shared/scheduler'; -import { InvalidAccountError } from './errors'; + +export type AccountFetchError = AccountDataError['error'] | 'cancelled'; interface IAccountFetchWatcher { onFinish: () => void; - onError: (error: Error) => void; + onError: (error: AccountFetchError) => void; } // Account data is valid for 1 minute unless the account has expired. @@ -26,7 +33,7 @@ export default class AccountDataCache { private watchers: IAccountFetchWatcher[] = []; constructor( - private fetchHandler: (token: AccountToken) => Promise<IAccountData>, + private fetchHandler: (token: AccountToken) => Promise<AccountDataResponse>, private updateHandler: (data?: IAccountData) => void, ) {} @@ -64,7 +71,7 @@ export default class AccountDataCache { this.validUntil = undefined; this.updateHandler(); this.notifyWatchers((watcher) => { - watcher.onError(new Error('Cancelled')); + watcher.onError('cancelled'); }); } @@ -94,16 +101,20 @@ export default class AccountDataCache { private async performFetch(accountToken: AccountToken) { this.performingFetch = true; - try { - // it's possible for invalidate() to be called or for a fetch for a different account token - // to start before this fetch completes, so checking if the current account token is the one - // used is necessary below. - const accountData = await this.fetchHandler(accountToken); - + // it's possible for invalidate() to be called or for a fetch for a different account token + // to start before this fetch completes, so checking if the current account token is the one + // used is necessary below. + const response = await this.fetchHandler(accountToken); + if ('error' in response) { if (this.currentAccount === accountToken) { - this.setValue(accountData); + this.handleFetchError(accountToken, response.error); + this.performingFetch = false; + } + } else { + if (this.currentAccount === accountToken) { + this.setValue(response); - const refetchDelay = this.calculateRefetchDelay(accountData.expiry); + const refetchDelay = this.calculateRefetchDelay(response.expiry); if (refetchDelay) { this.scheduleFetch(accountToken, refetchDelay); } @@ -111,12 +122,6 @@ export default class AccountDataCache { this.waitStrategy.reset(); this.performingFetch = false; } - } catch (e) { - const error = e as Error; - if (this.currentAccount === accountToken) { - this.handleFetchError(accountToken, error); - this.performingFetch = false; - } } } @@ -131,9 +136,9 @@ export default class AccountDataCache { } } - private handleFetchError(accountToken: AccountToken, error: Error) { + private handleFetchError(accountToken: AccountToken, error: AccountDataError['error']) { this.notifyWatchers((w) => w.onError(error)); - if (!(error instanceof InvalidAccountError)) { + if (error !== 'invalid-account') { this.scheduleRetry(accountToken); } } diff --git a/gui/src/main/account.ts b/gui/src/main/account.ts index 6ee2aafcde..1690445fb2 100644 --- a/gui/src/main/account.ts +++ b/gui/src/main/account.ts @@ -1,5 +1,6 @@ import { closeToExpiry } from '../shared/account-expiry'; import { + AccountDataError, AccountToken, DeviceEvent, DeviceState, @@ -7,7 +8,6 @@ import { IDeviceRemoval, TunnelState, } from '../shared/daemon-rpc-types'; -import { messages } from '../shared/gettext'; import log from '../shared/logging'; import { AccountExpiredNotificationProvider, @@ -17,7 +17,6 @@ import { import { Scheduler } from '../shared/scheduler'; import AccountDataCache from './account-data-cache'; import { DaemonRpc } from './daemon-rpc'; -import { InvalidAccountError } from './errors'; import { IpcMainEventChannel } from './ipc-event-channel'; import { NotificationSender } from './notification-controller'; import { TunnelStateProvider } from './tunnel-state'; @@ -70,7 +69,9 @@ export default class Account { public registerIpcListeners() { IpcMainEventChannel.account.handleCreate(() => this.createNewAccount()); - IpcMainEventChannel.account.handleLogin((token: AccountToken) => this.login(token)); + IpcMainEventChannel.account.handleLogin( + async (token: AccountToken) => (await this.login(token)) ?? undefined, + ); IpcMainEventChannel.account.handleLogout(() => this.logout()); IpcMainEventChannel.account.handleGetWwwAuthToken(() => this.daemonRpc.getWwwAuthToken()); IpcMainEventChannel.account.handleSubmitVoucher(async (voucherCode: string) => { @@ -162,18 +163,12 @@ export default class Account { } } - private async login(accountToken: AccountToken): Promise<void> { - try { - await this.daemonRpc.loginAccount(accountToken); - } catch (e) { - const error = e as Error; - log.error(`Failed to login: ${error.message}`); + private async login(accountToken: AccountToken): Promise<AccountDataError | void> { + const error = await this.daemonRpc.loginAccount(accountToken); - if (error instanceof InvalidAccountError) { - throw Error(messages.gettext('Invalid account number')); - } else { - throw error; - } + if (error) { + log.error(`Failed to login: ${error.error}`); + return error; } } diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 40794e765c..f3b744e522 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -8,6 +8,8 @@ import { import { promisify } from 'util'; import { + AccountDataError, + AccountDataResponse, AccountToken, AfterDisconnect, AuthFailedError, @@ -23,7 +25,6 @@ import { ErrorStateCause, FirewallPolicyError, FirewallPolicyErrorType, - IAccountData, IAppVersionInfo, IBridgeConstraints, IDevice, @@ -61,12 +62,6 @@ import { VoucherResponse, } from '../shared/daemon-rpc-types'; import log from '../shared/logging'; -import { - CommunicationError, - InvalidAccountError, - ListDevicesError, - TooManyDevicesError, -} from './errors'; import { ManagementServiceClient } from './management_interface/management_interface_grpc_pb'; import * as grpcTypes from './management_interface/management_interface_pb'; @@ -200,22 +195,22 @@ export class DaemonRpc { } } - public async getAccountData(accountToken: AccountToken): Promise<IAccountData> { + public async getAccountData(accountToken: AccountToken): Promise<AccountDataResponse> { try { const response = await this.callString<grpcTypes.AccountData>( this.client.getAccountData, accountToken, ); const expiry = response.getExpiry()!.toDate().toISOString(); - return { expiry }; + return { type: 'success', expiry }; } catch (e) { const error = e as grpc.ServiceError; if (error.code) { switch (error.code) { case grpc.status.UNAUTHENTICATED: - throw new InvalidAccountError(); + return { type: 'error', error: 'invalid-account' }; default: - throw new CommunicationError(); + return { type: 'error', error: 'communication' }; } } throw error; @@ -277,18 +272,18 @@ export class DaemonRpc { return response.getValue(); } - public async loginAccount(accountToken: AccountToken): Promise<void> { + public async loginAccount(accountToken: AccountToken): Promise<AccountDataError | void> { try { await this.callString(this.client.loginAccount, accountToken); } catch (e) { const error = e as grpc.ServiceError; switch (error.code) { case grpc.status.RESOURCE_EXHAUSTED: - throw new TooManyDevicesError(); + return { type: 'error', error: 'too-many-devices' }; case grpc.status.UNAUTHENTICATED: - throw new InvalidAccountError(); + return { type: 'error', error: 'invalid-account' }; default: - throw new CommunicationError(); + return { type: 'error', error: 'communication' }; } } } @@ -603,7 +598,7 @@ export class DaemonRpc { return response.getDevicesList().map(convertFromDevice); } catch { - throw new ListDevicesError(); + throw new Error('Failed to list devices'); } } diff --git a/gui/src/main/errors.ts b/gui/src/main/errors.ts deleted file mode 100644 index 86b482f7bd..0000000000 --- a/gui/src/main/errors.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { messages } from '../shared/gettext'; - -export class InvalidAccountError extends Error { - constructor() { - super( - // TRANSLATORS: Error message shown above login input when trying to login with a non-existent - // TRANSLATORS: account number. - messages.pgettext('login-view', 'Invalid account number'), - ); - } -} - -export class CommunicationError extends Error { - constructor() { - super('api.mullvad.net is blocked, please check your firewall'); - } -} - -export class TooManyDevicesError extends Error { - constructor() { - super( - // TRANSLATORS: Error message shown above login input when trying to login to an account with - // TRANSLATORS: too many registered devices. - messages.pgettext('login-view', 'Too many devices'), - ); - } -} - -export class ListDevicesError extends Error { - constructor() { - super( - // TRANSLATORS: Error message shown above login input when trying to login but the app fails - // TRANSLATORS: to fetch the list of registered devices. - messages.pgettext('login-view', 'Failed to fetch list of devices'), - ); - } -} |
