@@ -34,6 +34,13 @@ interface FetchRequestOptions {
3434 body ?: string ;
3535}
3636
37+ const MAX_RETRY_ATTEMPTS = 3 ;
38+ const BACKOFF_MULTIPLIER = 1.5 ;
39+ const MINIMUM_SLEEP_TIME = 500 ;
40+ const RETRY_STATUS_CODES = [ 500 , 502 , 504 ] ;
41+
42+ const sleep = ( ms : number ) => new Promise ( resolve => setTimeout ( resolve , ms ) )
43+
3744export default class ApiClient implements HttpClient {
3845 private config : HttpClientConfig ;
3946
@@ -44,51 +51,85 @@ export default class ApiClient implements HttpClient {
4451 public async get ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
4552 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "GET" , requestOptions ) ;
4653
47- /* @ts -ignore */
48- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
49- if ( ! response . ok ) {
50- throw this . buildError ( await response . json ( ) ) ;
51- }
54+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
5255
5356 return this . parseResponse ( response ) ;
5457 }
5558
5659 public async delete ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
5760 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "DELETE" , requestOptions ) ;
5861
59- /* @ts -ignore */
60- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
61- if ( ! response . ok ) {
62- throw this . buildError ( await response . json ( ) ) ;
63- }
62+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
6463
6564 return this . parseResponse ( response ) ;
6665 }
6766
6867 public async post ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
6968 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "POST" , requestOptions ) ;
7069
71- /* @ts -ignore */
72- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
73- if ( ! response . ok ) {
74- throw this . buildError ( await response . json ( ) ) ;
75- }
70+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
7671
7772 return this . parseResponse ( response ) ;
7873 }
7974
8075 public async put ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
8176 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "PUT" , requestOptions ) ;
8277
83- /* @ts -ignore */
84- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
85- if ( ! response . ok ) {
86- throw this . buildError ( await response . json ( ) ) ;
87- }
78+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
8879
8980 return this . parseResponse ( response ) ;
9081 }
9182
83+ private async fetchWithRetry ( requestUrl : string , fetchRequestOptions : FetchRequestOptions ) : Promise < any > {
84+ let response : any = null ;
85+ let requestError : any = null ;
86+ let retryAttempts = 1 ;
87+
88+ const makeRequest = async ( ) : Promise < any > => {
89+ try {
90+ response = await fetch ( requestUrl , fetchRequestOptions ) ;
91+ } catch ( e ) {
92+ requestError = e ;
93+ }
94+
95+ if ( this . shouldRetryRequest ( response , requestError , retryAttempts ) ) {
96+ retryAttempts ++ ;
97+ await sleep ( this . getSleepTime ( retryAttempts ) ) ;
98+ return makeRequest ( ) ;
99+ }
100+
101+ if ( ! response . ok ) {
102+ throw this . buildError ( await response . json ( ) ) ;
103+ }
104+
105+ return response ;
106+ }
107+
108+ return makeRequest ( ) ;
109+ }
110+
111+ private shouldRetryRequest ( response : any , requestError : any , retryAttempt : number ) : boolean {
112+ if ( retryAttempt > MAX_RETRY_ATTEMPTS ) {
113+ return false ;
114+ }
115+
116+ if ( requestError != null && requestError instanceof TypeError ) {
117+ return true ;
118+ }
119+
120+ if ( response != null && RETRY_STATUS_CODES . includes ( response . status ) ) {
121+ return true ;
122+ }
123+
124+ return false ;
125+ }
126+
127+ private getSleepTime ( retryAttempt : number ) : number {
128+ let sleepTime = MINIMUM_SLEEP_TIME * Math . pow ( BACKOFF_MULTIPLIER , retryAttempt ) ;
129+ const jitter = Math . random ( ) + 0.5 ;
130+ return sleepTime * jitter ;
131+ }
132+
92133 private buildRequestUrlAndOptions ( method : FetchRequestOptions [ "method" ] , requestOptions ?: HttpClientRequestOptions ) : [ string , FetchRequestOptions ] {
93134 let baseUrl = this . config . baseUrl ;
94135 const fetchRequestOptions : FetchRequestOptions = {
0 commit comments