import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Inject, Injectable, Optional } from '@angular/core'; import { MonoTypeOperatorFunction, Observable, throwError, timer } from 'rxjs'; import { catchError, mergeMap, retryWhen, tap } from 'rxjs/operators'; import { MAX_RETRIES_ON_SERVER_ERROR } from './max-retries.token'; import { ErrorService } from './error.service'; function updateSeconds(seconds: number) { if (seconds === 0 || seconds === 1) { return seconds + 1; } else { return seconds * seconds; } } function backoffOnServerError(maxRetries = 3): MonoTypeOperatorFunction> { let seconds = 0; return retryWhen(attempts => attempts.pipe( tap(() => (seconds = updateSeconds(seconds))), mergeMap((error: HttpErrorResponse, index) => { if ((error.status < 500 && error.status !== 0) || index === maxRetries) { return throwError(error); } else { console.error('An error occurred: ', error); console.error(`Retrying in ${seconds} seconds...`); return timer(seconds * 1000); } }), ), ); } @Injectable() export class ServerErrorInterceptor implements HttpInterceptor { constructor( private readonly _errorService: ErrorService, @Optional() @Inject(MAX_RETRIES_ON_SERVER_ERROR) private readonly _maxRetries: number, ) {} intercept(req: HttpRequest, next: HttpHandler): Observable> { return next.handle(req).pipe( catchError((error: HttpErrorResponse) => { if (error.status >= 500 || error.status === 0) { this._errorService.set(error); } return throwError(error); }), backoffOnServerError(this._maxRetries || 3), ); } }