summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-06-13 16:03:05 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-06-15 08:49:42 -0300
commit47454b7fe8341997e2580811e4aed00084fb741c (patch)
tree4532087665b22ec76a9170f0addefe7026467e4b
parentffc9500d5d57c427378591aab5e4c86960d27fe7 (diff)
downloadmullvadvpn-47454b7fe8341997e2580811e4aed00084fb741c.tar.xz
mullvadvpn-47454b7fe8341997e2580811e4aed00084fb741c.zip
Split `BackendError` into individual error classes
-rw-r--r--app/app.js10
-rw-r--r--app/components/Connect.js17
-rw-r--r--app/lib/backend.js127
3 files changed, 78 insertions, 76 deletions
diff --git a/app/app.js b/app/app.js
index 055972fdf5..6ec6ac643f 100644
--- a/app/app.js
+++ b/app/app.js
@@ -9,7 +9,7 @@ import { webFrame, ipcRenderer } from 'electron';
import { log } from './lib/platform';
import makeRoutes from './routes';
import configureStore from './redux/store';
-import { Backend, BackendError } from './lib/backend';
+import { Backend, NoAccountError } from './lib/backend';
import { setShutdownHandler } from './shutdown-handler';
@@ -33,11 +33,9 @@ ipcRenderer.on('backend-info', async (_event, args) => {
await backend.fetchSecurityState();
await backend.connect();
} catch (e) {
- if (e instanceof BackendError) {
- if (e.type === 'NO_ACCOUNT') {
- log.debug('No user set in the backend, showing window');
- ipcRenderer.send('show-window');
- }
+ if (e instanceof NoAccountError) {
+ log.debug('No user set in the backend, showing window');
+ ipcRenderer.send('show-window');
}
}
});
diff --git a/app/components/Connect.js b/app/components/Connect.js
index 3fbe04cefa..f4647678f1 100644
--- a/app/components/Connect.js
+++ b/app/components/Connect.js
@@ -9,7 +9,7 @@ import { TransparentButton, RedTransparentButton, GreenButton, Label } from './s
import Accordion from './Accordion';
import styles from './ConnectStyles';
-import { BackendError } from '../lib/backend';
+import { NoCreditError, NoInternetError } from '../lib/backend';
import Map from './Map';
import type { HeaderBarStyle } from './HeaderBar';
@@ -79,15 +79,18 @@ export default class Connect extends Component<ConnectProps, ConnectState> {
);
}
- renderError(error: BackendError) {
+ renderError(error: Error) {
+ const title = error.userFriendlyTitle || 'Something went wrong';
+ const message = error.userFriendlyMessage || error.message;
+
return (
<View style={styles.connect}>
<View style={styles.status_icon}>
<Img source="icon-fail" height={60} width={60} alt="" />
</View>
<View style={styles.status}>
- <View style={styles.error_title}>{error.title}</View>
- <View style={styles.error_message}>{error.message}</View>
+ <View style={styles.error_title}>{title}</View>
+ <View style={styles.error_message}>{message}</View>
{error.type === 'NO_CREDIT' ? (
<View>
<GreenButton onPress={this.onExternalLink.bind(this, 'purchase')}>
@@ -339,16 +342,16 @@ export default class Connect extends Component<ConnectProps, ConnectState> {
return classes;
}
- displayError(): ?BackendError {
+ displayError(): ?Error {
// Offline?
if (!this.props.connection.isOnline) {
- return new BackendError('NO_INTERNET');
+ return new NoInternetError();
}
// No credit?
const expiry = this.props.accountExpiry;
if (expiry && moment(expiry).isSameOrBefore(moment())) {
- return new BackendError('NO_CREDIT');
+ return new NoCreditError();
}
return null;
diff --git a/app/lib/backend.js b/app/lib/backend.js
index 89b8945962..c7b4ca53ec 100644
--- a/app/lib/backend.js
+++ b/app/lib/backend.js
@@ -12,64 +12,65 @@ import type { ReduxStore } from '../redux/store';
import type { AccountToken, BackendState, RelaySettingsUpdate } from './ipc-facade';
import type { ConnectionState } from '../redux/connection/reducers';
-export type ErrorType =
- | 'NO_CREDIT'
- | 'NO_INTERNET'
- | 'NO_DAEMON'
- | 'INVALID_ACCOUNT'
- | 'NO_ACCOUNT'
- | 'COMMUNICATION_FAILURE'
- | 'UNKNOWN_ERROR';
+export class NoCreditError extends Error {
+ constructor() {
+ super("Account doesn't have enough credit available for connection");
+ }
-export class BackendError extends Error {
- type: ErrorType;
- title: string;
- message: string;
- cause: ?Error;
+ get userFriendlyTitle(): string {
+ return 'Out of time';
+ }
- constructor(type: ErrorType, cause?: Error) {
- super('');
- this.type = type;
- this.title = BackendError.localizedTitle(type);
- this.message = BackendError.localizedMessage(type, cause);
- this.cause = cause;
+ get userFriendlyMessage(): string {
+ return 'Buy more time, so you can continue using the internet securely';
}
+}
- static localizedTitle(type: ErrorType): string {
- switch (type) {
- case 'NO_CREDIT':
- return 'Out of time';
- case 'NO_INTERNET':
- return 'Offline';
- default:
- return 'Something went wrong';
- }
+export class NoInternetError extends Error {
+ constructor() {
+ super('Internet connectivity is currently unavailable');
}
- static localizedMessage(type: ErrorType, cause: ?Error): string {
- // TODO: since instanceof now works, BackendError can be replaced by a set
- // of specific error types
- switch (type) {
- case 'NO_CREDIT':
- return 'Buy more time, so you can continue using the internet securely';
- case 'NO_INTERNET':
- return 'Your internet connection will be secured when you get back online';
- case 'INVALID_ACCOUNT':
- return 'Invalid account number';
- case 'NO_ACCOUNT':
- return 'No account was set';
- case 'NO_DAEMON':
- return 'Could not connect to the Mullvad daemon';
- case 'COMMUNICATION_FAILURE':
- return 'api.mullvad.net is blocked, please check your firewall';
- case 'UNKNOWN_ERROR': {
- const message = cause ? ', ' + cause.message : '';
+ get userFriendlyTitle(): string {
+ return 'Offline';
+ }
- return 'An unknown error occurred' + message;
- }
- default:
- return '';
- }
+ get userFriendlyMessage(): string {
+ return 'Your internet connection will be secured when you get back online';
+ }
+}
+
+export class NoDaemonError extends Error {
+ constructor() {
+ super('Could not connect to Mullvad daemon');
+ }
+}
+
+export class InvalidAccountError extends Error {
+ constructor() {
+ super('Invalid account number');
+ }
+}
+
+export class NoAccountError extends Error {
+ constructor() {
+ super('No account was set');
+ }
+}
+
+export class CommunicationError extends Error {
+ constructor() {
+ super('api.mullvad.net is blocked, please check your firewall');
+ }
+}
+
+export class UnknownError extends Error {
+ constructor(cause: string) {
+ super(`An unknown error occurred, ${cause}`);
+ }
+
+ get userFriendlyTitle(): string {
+ return 'Something went wrong';
}
}
@@ -187,30 +188,30 @@ export class Backend {
} catch (e) {
log.error('Failed to log in,', e.message);
- const err = this._rpcErrorToBackendError(e);
- this._store.dispatch(accountActions.loginFailed(err));
+ const error = this._rpcErrorToBackendError(e);
+ this._store.dispatch(accountActions.loginFailed(error));
}
}
_rpcErrorToBackendError(e) {
- if (e instanceof BackendError) {
- return e;
- } else if (e instanceof JsonRpcError) {
+ if (e instanceof JsonRpcError) {
switch (e.code) {
case -200: // Account doesn't exist
- return new BackendError('INVALID_ACCOUNT');
+ return new InvalidAccountError();
case -32603: // Internal error
// We treat all internal backend errors as the user cannot reach
// api.mullvad.net. This is not always true of course, but it is
// true so often that we choose to disregard the other edge cases
// for now.
- return new BackendError('COMMUNICATION_FAILURE');
+ return new CommunicationError();
}
} else if (e instanceof TimeOutError) {
- return new BackendError('COMMUNICATION_FAILURE');
+ return new CommunicationError();
+ } else if (e instanceof NoDaemonError) {
+ return e;
}
- return new BackendError('UNKNOWN_ERROR', e);
+ return new UnknownError(e.message);
}
async autologin() {
@@ -223,7 +224,7 @@ export class Backend {
const accountToken = await this._ipc.getAccount();
if (!accountToken) {
- throw new BackendError('NO_ACCOUNT');
+ throw new NoAccountError();
}
log.debug('The backend had an account number stored: ', accountToken);
@@ -361,7 +362,7 @@ export class Backend {
const accountToken = await this._ipc.getAccount();
if (!accountToken) {
- throw new BackendError('NO_ACCOUNT');
+ throw new NoAccountError();
}
const accountData = await ipc.getAccountData(accountToken);
@@ -527,7 +528,7 @@ export class Backend {
}
return this._authenticationPromise;
} else {
- return Promise.reject(new BackendError('NO_DAEMON'));
+ return Promise.reject(new NoDaemonError());
}
}