Skip to content

Commit

Permalink
Error handling (#123)
Browse files Browse the repository at this point in the history
* error handling update for API responses, with no change on SDK response

* error handling further changes

* fix error type and getAlises func

* fix getAliases

* update

* update
  • Loading branch information
mjadach-iv authored Aug 22, 2024
1 parent 3488046 commit d2a4605
Show file tree
Hide file tree
Showing 92 changed files with 749 additions and 722 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
run: yarn build

- name: Linting
run: yarn lint
run: yarn lint:ci

- name: Formatting
run: yarn format:ci
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ async function getResource(payload: ResourcePayloadType) {
return parseResponse(rawResponse);
}

// check if response has the structure of an expected api error
// check if response has the structure of an expected sdk api error
if (isApiErrorResponse(rawResponse)) {
throw new APIError();
throw new sdkApiError();
}

// if we get to this point then we could not parse the response
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hoprnet/hopr-sdk",
"version": "2.1.4",
"version": "2.1.5-beta.1",
"description": "A SDK for HOPRd's Rest API functions",
"author": "HOPR Association",
"license": "GPL-3.0",
Expand Down Expand Up @@ -34,6 +34,7 @@
"build": "tsup",
"pack": "tsup && yarn pack",
"lint": "tsc",
"lint:ci": "tsc",
"format": "prettier --write src/ e2e/ .github/ *.js *.ts *.json *.md",
"format:ci": "prettier --check src/ e2e/ .github/ *.js *.ts *.json *.md",
"test": "jest --coverage src/",
Expand Down
17 changes: 7 additions & 10 deletions src/api/account/getAddresses.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ZodError } from 'zod';
import { APIError } from '../../utils';
import { sdkApiError } from '../../utils';
import { getAddresses } from './getAddresses';
import nock from 'nock';
import { GetAddressesResponseType } from '../../types';
Expand Down Expand Up @@ -32,8 +32,7 @@ describe('getAddresses', () => {
test('should return 403 if authentication failed', async function () {
const invalidApiToken = 'Not valid';
const expectedResponse = {
status: 403,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'authentication failed'
};

Expand All @@ -43,13 +42,12 @@ describe('getAddresses', () => {

await expect(
getAddresses({ apiEndpoint: API_ENDPOINT, apiToken: invalidApiToken })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return 403 if authorization fails', async function () {
const expectedResponse = {
status: 403,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'You are not authorized to perform this action'
};

Expand All @@ -59,13 +57,12 @@ describe('getAddresses', () => {

await expect(
getAddresses({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return 422 if there is an unknown failure', async function () {
const expectedResponse = {
status: 422,
statusText: 'UNKNOWN_FAILURE',
status: 'UNKNOWN_FAILURE',
error: 'Full error message.'
};

Expand All @@ -75,7 +72,7 @@ describe('getAddresses', () => {

await expect(
getAddresses({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return ZodError if there is a parsing error', async function () {
Expand Down
14 changes: 9 additions & 5 deletions src/api/account/getAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {
GetAddressesResponse,
GetAddressesResponseType,
BasePayloadType,
APIErrorResponse
ApiErrorResponse
} from '../../types';
import { APIError, fetchWithTimeout, getHeaders } from '../../utils';
import { sdkApiError, fetchWithTimeout, getHeaders } from '../../utils';

/**
* Gets the HOPR and native addresses associated to the node.
Expand Down Expand Up @@ -42,12 +42,16 @@ export const getAddresses = async (
}

// check if response has the structure of an expected api error
const isApiErrorResponse = APIErrorResponse.safeParse(jsonResponse);
const isApiErrorResponse = ApiErrorResponse.safeParse(jsonResponse);

if (isApiErrorResponse.success) {
throw new APIError(isApiErrorResponse.data);
throw new sdkApiError({
status: rawResponse.status,
statusText: isApiErrorResponse.data.status,
hoprdErrorPayload: isApiErrorResponse.data
});
}

// we could not parse the response and it is not unexpected
// we could not parse the response and it is unexpected
throw new ZodError(parsedRes.error.issues);
};
17 changes: 7 additions & 10 deletions src/api/account/getBalances.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import nock from 'nock';
import { getBalances } from './getBalances';
import { APIError } from '../../utils';
import { sdkApiError } from '../../utils';
import { GetBalancesResponseType } from '../../types';

const API_ENDPOINT = 'http://localhost:3001';
Expand Down Expand Up @@ -31,43 +31,40 @@ describe('getBalances', () => {

it('should return 401 if authentication fails', async () => {
const response = {
status: 401,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'authentication failed'
};

nock(API_ENDPOINT).get('/api/v3/account/balances').reply(401, response);

await expect(
getBalances({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

it('should return 403 if authorization fails', async () => {
const response = {
status: 403,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'You are not authorized to perform this action'
};

nock(API_ENDPOINT).get('/api/v3/account/balances').reply(403, response);

await expect(
getBalances({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

it('should return 422 if unknown failure', async () => {
const response = {
status: 422,
statusText: 'UNKNOWN_FAILURE',
status: 'UNKNOWN_FAILURE',
error: 'Full error message.'
};

nock(API_ENDPOINT).get('/api/v3/account/balances').reply(422, response);

await expect(
getBalances({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});
});
16 changes: 10 additions & 6 deletions src/api/account/getBalances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {
GetBalancesResponseType,
GetBalancesResponse,
BasePayloadType,
APIErrorResponse
ApiErrorResponse
} from '../../types';
import { APIError, fetchWithTimeout, getHeaders } from '../../utils';
import { sdkApiError, fetchWithTimeout, getHeaders } from '../../utils';

/**
* Fetches the HOPR and native balances of the node.
Expand All @@ -30,7 +30,7 @@ export const getBalances = async (

// received unexpected error from server
if (rawResponse.status !== 200) {
throw new APIError({
throw new sdkApiError({
status: rawResponse.status,
statusText: rawResponse.statusText
});
Expand Down Expand Up @@ -58,12 +58,16 @@ export const getBalances = async (
}

// check if response has the structure of an expected api error
const isApiErrorResponse = APIErrorResponse.safeParse(jsonResponse);
const isApiErrorResponse = ApiErrorResponse.safeParse(jsonResponse);

if (isApiErrorResponse.success) {
throw new APIError(isApiErrorResponse.data);
throw new sdkApiError({
status: rawResponse.status,
statusText: isApiErrorResponse.data.status,
hoprdErrorPayload: isApiErrorResponse.data
});
}

// we could not parse the response and it is not unexpected
// we could not parse the response and it is unexpected
throw new ZodError(parsedRes.error.issues);
};
17 changes: 7 additions & 10 deletions src/api/account/getHoprAddress.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import nock from 'nock';
import { getHoprAddress } from './getHoprAddress';
import { APIError } from '../../utils';
import { sdkApiError } from '../../utils';
import { GetAddressesResponseType } from '../../types';

const API_ENDPOINT = 'http://localhost:3001';
Expand Down Expand Up @@ -31,8 +31,7 @@ describe('getHoprAddress', () => {
test('should return 401 if authentication failed', async function () {
const invalidApiToken = 'Not valid';
const expectedResponse = {
status: 401,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'authentication failed'
};

Expand All @@ -42,13 +41,12 @@ describe('getHoprAddress', () => {

await expect(
getHoprAddress({ apiEndpoint: API_ENDPOINT, apiToken: invalidApiToken })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return 403 if authorization fails', async function () {
const expectedResponse = {
status: 403,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'You are not authorized to perform this action'
};

Expand All @@ -58,13 +56,12 @@ describe('getHoprAddress', () => {

await expect(
getHoprAddress({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return 422 if there is an unknown failure', async function () {
const expectedResponse = {
status: 422,
statusText: 'UNKNOWN_FAILURE',
status: 'UNKNOWN_FAILURE',
error: 'Full error message.'
};

Expand All @@ -74,6 +71,6 @@ describe('getHoprAddress', () => {

await expect(
getHoprAddress({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});
});
17 changes: 7 additions & 10 deletions src/api/account/getHoprBalance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import nock from 'nock';
import { getHoprBalance } from './getHoprBalance';
import { APIError } from '../../utils';
import { sdkApiError } from '../../utils';
import { GetBalancesResponseType } from '../../types';

const API_ENDPOINT = 'http://localhost:3001';
Expand Down Expand Up @@ -31,43 +31,40 @@ describe('getHoprBalance', () => {

it('should return 401 if authentication fails', async () => {
const response = {
status: 401,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'authentication failed'
};

nock(API_ENDPOINT).get('/api/v3/account/balances').reply(401, response);

await expect(
getHoprBalance({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

it('should return 403 if authorization fails', async () => {
const response = {
status: 403,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'You are not authorized to perform this action'
};

nock(API_ENDPOINT).get('/api/v3/account/balances').reply(403, response);

await expect(
getHoprBalance({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

it('should return 422 if unknown failure', async () => {
const response = {
status: 422,
statusText: 'UNKNOWN_FAILURE',
status: 'UNKNOWN_FAILURE',
error: 'Full error message.'
};

nock(API_ENDPOINT).get('/api/v3/account/balances').reply(422, response);

await expect(
getHoprBalance({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});
});
17 changes: 7 additions & 10 deletions src/api/account/getNativeAddress.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import nock from 'nock';
import { getNativeAddress } from './getNativeAddress';
import { APIError } from '../../utils';
import { sdkApiError } from '../../utils';
import { GetAddressesResponseType } from '../../types';

const API_ENDPOINT = 'http://localhost:3001';
Expand Down Expand Up @@ -31,8 +31,7 @@ describe('getNativeAddress', () => {
test('should return 401 if authentication failed', async function () {
const invalidApiToken = 'Not valid';
const expectedResponse = {
status: 401,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'authentication failed'
};

Expand All @@ -42,13 +41,12 @@ describe('getNativeAddress', () => {

await expect(
getNativeAddress({ apiEndpoint: API_ENDPOINT, apiToken: invalidApiToken })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return 403 if authorization fails', async function () {
const expectedResponse = {
status: 403,
statusText: 'UNAUTHORIZED',
status: 'UNAUTHORIZED',
error: 'You are not authorized to perform this action'
};

Expand All @@ -58,13 +56,12 @@ describe('getNativeAddress', () => {

await expect(
getNativeAddress({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});

test('should return 422 if there is an unknown failure', async function () {
const expectedResponse = {
status: 422,
statusText: 'UNKNOWN_FAILURE',
status: 'UNKNOWN_FAILURE',
error: 'Full error message.'
};

Expand All @@ -74,6 +71,6 @@ describe('getNativeAddress', () => {

await expect(
getNativeAddress({ apiEndpoint: API_ENDPOINT, apiToken: API_TOKEN })
).rejects.toThrow(APIError);
).rejects.toThrow(sdkApiError);
});
});
Loading

0 comments on commit d2a4605

Please sign in to comment.