All files / src api_resource.ts

100% Statements 31/31
100% Branches 16/16
100% Functions 10/10
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103                            12x       12x   12x   12x   12x         953x 953x       1x       11x               8x 7x 7x     7x 2x   5x 1x     5x         1x               13x 2x   11x 10x 10x         8x 8x           2x   1x     13x 5x 4x       8x      
/*
 * Copyright © 2019 Lisk Foundation
 *
 * See the LICENSE file at the top-level directory of this distribution
 * for licensing information.
 *
 * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
 * no part of this software, including this file, may be copied, modified,
 * propagated, or distributed except according to the terms contained in the
 * LICENSE file.
 *
 * Removal or modification of this copyright notice is prohibited.
 *
 */
import Axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
 
import { APIClient } from './api_client';
import { APIErrorResponse, APIResponse, HashMap } from './api_types';
import { APIError } from './errors';
 
const API_RECONNECT_MAX_RETRY_COUNT = 3;
 
const REQUEST_RETRY_TIMEOUT = 1000;
 
export class APIResource {
	public apiClient: APIClient;
	public path: string;
 
	public constructor(apiClient: APIClient) {
		this.apiClient = apiClient;
		this.path = '';
	}
 
	public get headers(): HashMap {
		return this.apiClient.headers;
	}
 
	public get resourcePath(): string {
		return `${this.apiClient.currentNode}/api${this.path}`;
	}
 
	public async handleRetry(
		error: Error,
		req: AxiosRequestConfig,
		retryCount: number,
	): Promise<APIResponse> {
		if (this.apiClient.hasAvailableNodes()) {
			return new Promise<APIResponse>(resolve =>
				setTimeout(resolve, REQUEST_RETRY_TIMEOUT),
			).then(
				async (): Promise<APIResponse> => {
					if (retryCount > API_RECONNECT_MAX_RETRY_COUNT) {
						throw error;
					}
					if (this.apiClient.randomizeNodes) {
						this.apiClient.banActiveNodeAndSelect();
					}
 
					return this.request(req, true, retryCount + 1);
				},
			);
		}
 
		throw error;
	}
 
	public async request(
		req: AxiosRequestConfig,
		retry: boolean,
		retryCount: number = 1,
	): Promise<APIResponse> {
		const request = Axios.request(req)
			.then((res: AxiosResponse) => res.data)
			.catch((error: AxiosError): void => {
				if (error.response) {
					const { status } = error.response;
					if (error.response.data) {
						const {
							error: errorString,
							errors,
							message,
						}: APIErrorResponse = error.response.data;
						throw new APIError(
							message || errorString || 'An unknown error has occurred.',
							status,
							errors,
						);
					}
					throw new APIError('An unknown error has occurred.', status);
				}
				throw error;
			});
 
		if (retry) {
			return request.catch(async (err: Error) =>
				this.handleRetry(err, req, retryCount),
			);
		}
 
		return request;
	}
}