@@ -34,6 +34,12 @@ 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+
41+ const sleep = ( ms : number ) => new Promise ( resolve => setTimeout ( resolve , ms ) )
42+
3743export default class ApiClient implements HttpClient {
3844 private config : HttpClientConfig ;
3945
@@ -44,51 +50,85 @@ export default class ApiClient implements HttpClient {
4450 public async get ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
4551 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "GET" , requestOptions ) ;
4652
47- /* @ts -ignore */
48- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
49- if ( ! response . ok ) {
50- throw this . buildError ( await response . json ( ) ) ;
51- }
53+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
5254
5355 return this . parseResponse ( response ) ;
5456 }
5557
5658 public async delete ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
5759 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "DELETE" , requestOptions ) ;
5860
59- /* @ts -ignore */
60- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
61- if ( ! response . ok ) {
62- throw this . buildError ( await response . json ( ) ) ;
63- }
61+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
6462
6563 return this . parseResponse ( response ) ;
6664 }
6765
6866 public async post ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
6967 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "POST" , requestOptions ) ;
7068
71- /* @ts -ignore */
72- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
73- if ( ! response . ok ) {
74- throw this . buildError ( await response . json ( ) ) ;
75- }
69+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
7670
7771 return this . parseResponse ( response ) ;
7872 }
7973
8074 public async put ( requestOptions : HttpClientRequestOptions ) : Promise < any > {
8175 const [ requestUrl , fetchRequestOptions ] = this . buildRequestUrlAndOptions ( "PUT" , requestOptions ) ;
8276
83- /* @ts -ignore */
84- const response = await fetch ( requestUrl , fetchRequestOptions ) ;
85- if ( ! response . ok ) {
86- throw this . buildError ( await response . json ( ) ) ;
87- }
77+ const response = await this . fetchWithRetry ( requestUrl , fetchRequestOptions ) ;
8878
8979 return this . parseResponse ( response ) ;
9080 }
9181
82+ private async fetchWithRetry ( requestUrl : string , fetchRequestOptions : FetchRequestOptions ) : Promise < any > {
83+ let response : any = null ;
84+ let requestError : any = null ;
85+ let retryAttempts = 1 ;
86+
87+ const makeRequest = async ( ) : Promise < any > => {
88+ try {
89+ response = await fetch ( requestUrl , fetchRequestOptions ) ;
90+ } catch ( e ) {
91+ requestError = e ;
92+ }
93+
94+ if ( this . shouldRetryRequest ( response , requestError , retryAttempts ) ) {
95+ retryAttempts ++ ;
96+ await sleep ( this . getSleepTime ( retryAttempts ) ) ;
97+ return makeRequest ( ) ;
98+ }
99+
100+ if ( ! response . ok ) {
101+ throw this . buildError ( await response . json ( ) ) ;
102+ }
103+
104+ return response ;
105+ }
106+
107+ return makeRequest ( ) ;
108+ }
109+
110+ private shouldRetryRequest ( response : any , requestError : any , retryAttempt : number ) : boolean {
111+ if ( retryAttempt > MAX_RETRY_ATTEMPTS ) {
112+ return false ;
113+ }
114+
115+ if ( requestError != null && requestError instanceof TypeError ) {
116+ return true ;
117+ }
118+
119+ if ( response ?. status == 502 ) {
120+ return true ;
121+ }
122+
123+ return false ;
124+ }
125+
126+ private getSleepTime ( retryAttempt : number ) : number {
127+ let sleepTime = MINIMUM_SLEEP_TIME * Math . pow ( BACKOFF_MULTIPLIER , retryAttempt ) ;
128+ const jitter = Math . random ( ) + 0.5 ;
129+ return sleepTime * jitter ;
130+ }
131+
92132 private buildRequestUrlAndOptions ( method : FetchRequestOptions [ "method" ] , requestOptions ?: HttpClientRequestOptions ) : [ string , FetchRequestOptions ] {
93133 let baseUrl = this . config . baseUrl ;
94134 const fetchRequestOptions : FetchRequestOptions = {
0 commit comments