...
 
Commits (3)
import api from '../../../src/common/services/api.service';
import api, { ApiError } from '../../../src/common/services/api.service';
import session from '../../../src/common/services/session.service';
import abortableFetch, {abort} from '../../../src/common/helpers/abortableFetch';
import { MINDS_API_URI } from '../../../src/config/Config';
......@@ -46,14 +46,14 @@ describe('api service POST', () => {
const res = await api.post('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
it('should return api error', async () => {
const response = { json: jest.fn(), ok: true };
const respBody = { status: 'error', error: 'some error' };
const respBody = { status: 500, error: 'some error' };
response.json.mockResolvedValue(respBody);
const params = {p1: 1, p2: 2};
......@@ -63,7 +63,7 @@ describe('api service POST', () => {
const res = await api.post('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......@@ -105,7 +105,7 @@ describe('api service GET', () => {
const res = await api.get('api/path', params, null);
} catch (err){
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
......@@ -122,7 +122,7 @@ describe('api service GET', () => {
const res = await api.get('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......@@ -167,7 +167,7 @@ describe('api service DELETE', () => {
const res = await api.delete('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
......@@ -184,7 +184,7 @@ describe('api service DELETE', () => {
const res = await api.delete('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......@@ -229,7 +229,7 @@ describe('api service PUT', () => {
const res = await api.put('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(response);
expect(err).toBeInstanceOf(ApiError);
}
});
......@@ -245,7 +245,7 @@ describe('api service PUT', () => {
const res = await api.put('api/path', params);
} catch (err) {
// assert on the error
expect(err).toEqual(respBody);
expect(err).toBeInstanceOf(ApiError);
}
});
});
......
......@@ -8,6 +8,15 @@ import abortableFetch from '../helpers/abortableFetch';
import { Version } from '../../config/Version';
import logService from './log.service';
/**
* Api Error
*/
export class ApiError extends Error {
constructor(...args) {
super(...args)
}
}
/**
* Api service
*/
......@@ -48,6 +57,22 @@ class ApiService {
}).join('&');
}
/**
* Throw an error
* @param {any} err
* @param {string} url
*/
_throwError(err, url) {
if (err instanceof Error) {
throw err;
}
const error = new ApiError(err.message || err.responseText || `Request error on: ${url}`);
if (err.status) {
error.status = err.status;
}
throw error;
}
/**
* Api get with abort support
* @param {string} url
......@@ -76,10 +101,12 @@ class ApiService {
// Bad authorization
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.get(url, params, tag);
if (refreshed) {
return await this.get(url, params, tag);
}
session.logout();
}
throw err;
this._throwError(err, url);
}
}
......@@ -104,11 +131,13 @@ class ApiService {
} catch(err) {
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.post(url, body);
if (refreshed) {
return await this.post(url, body);
}
logService.log('[ApiService] Token refresh failed: logout');
session.logout();
}
throw err;
this._throwError(err, url);
}
}
......@@ -133,19 +162,21 @@ class ApiService {
} catch(err) {
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.post(url, body);
if (refreshed) {
return await this.put(url, body);
}
logService.log('[ApiService] Token refresh failed: logout');
session.logout();
}
throw err;
this._throwError(err, url);
}
}
/**
* Upload file to s3, differences with generic upload are headers and formData (wich is not necessary)
* @param {any} lease
* @param {any} file
* @param {function} progress
* @param {any} lease
* @param {any} file
* @param {function} progress
*/
uploadToS3(lease, file, progress) {
......@@ -207,11 +238,13 @@ class ApiService {
} catch(err) {
if (err.status && err.status == 401) {
const refreshed = await session.badAuthorization(); //not actually a logout
if (refreshed) return await this.post(url, body);
if (refreshed) {
return await this.delete(url, body);
}
logService.log('[ApiService] Token refresh failed: logout');
session.logout();
}
throw err;
this._throwError(err, url);
}
}
......
......@@ -7,6 +7,7 @@ import settingsService from '../../settings/SettingsService'
import settingsStore from '../../settings/SettingsStore';
import { Sentry } from 'react-native-sentry';
import { isNetworkFail } from '../helpers/abortableFetch';
import { ApiError } from './api.service';
const parseErrorStack = error => {
if (!error || !error.stack) {
......@@ -67,7 +68,7 @@ class LogService {
prepend = null;
}
if (!isNetworkFail(error)) {
if (!isNetworkFail(error) && !(error instanceof ApiError && error.status === 401)) {
// report the issue to sentry
Sentry.captureException(error);
}
......