{ "version": 3, "sources": ["src/app/utils/rxjs-utils.ts"], "sourcesContent": ["import {\n BehaviorSubject,\n MonoTypeOperatorFunction,\n Observable,\n ObservableInput,\n ObservedValueOf,\n OperatorFunction,\n Subject,\n of,\n throwError,\n timer\n} from 'rxjs'\nimport { catchError, mergeMap, retryWhen, tap } from 'rxjs/operators'\n\nimport { HttpErrorResponse } from '@angular/common/http'\nimport { Router } from '@angular/router'\nimport { ReponseErrors } from '../soft-login/models/error.model'\nimport { Logger } from './logger'\nimport { RedirectTo } from './redirect-to'\n\nexport interface RetryOptions {\n retryTimes?: number;\n codesToRetryOn?: Array;\n delay?: number;\n}\nexport class RxJS {\n\n public static toBehaviorSubject(\n observable: Observable,\n defaultValue: T | null = null\n ): BehaviorSubject {\n const bSubject = new BehaviorSubject(defaultValue)\n\n const subscription = observable.subscribe(\n (value: T | null) => bSubject.next(value),\n error => bSubject.error(error),\n () => {\n subscription.unsubscribe()\n bSubject.complete()\n }\n )\n\n return bSubject\n }\n\n public static toSubject(observable: Observable): Subject {\n const subject = new Subject()\n\n const subscription = observable.subscribe(\n (value: T) => subject.next(value),\n error => subject.error(error),\n () => {\n subscription.unsubscribe()\n subject.complete()\n }\n )\n return subject\n }\n\n public static logAndRethrow\n >(logger: Logger): OperatorFunction> {\n return catchError(error => {\n logger.error(error)\n return throwError(() => error)\n })\n }\n\n /**\n * @returns a default implementation of the pipe catchError\n * which checks for the status to be 404 and returns @param defaultValue\n */\n public static catch404AndReturn\n (defaultValue: R): OperatorFunction>> {\n return catchError>((error) => {\n if (error?.status === 404) { return of(defaultValue) }\n return throwError(() => error)\n })\n }\n\n /**\n * @returns a default implementation of the pipe catchError\n * which checks for the status to be 401 and returns @param defaultValue\n */\n public static catch401AndReturn\n (defaultValue: R): OperatorFunction>> {\n return catchError>((error) => {\n if (error?.status === 401) { return of(defaultValue) }\n return throwError(() => error)\n })\n }\n\n\n /**\n * @returns a call to RxJS.retryWith with default parameters of:\n * delay: 500,\n * codesToRetryOn: [500],\n * retryTimes: 3\n */\n public static defaultRetryLogic(): MonoTypeOperatorFunction {\n return RxJS.retryWith({\n delay: 500,\n codesToRetryOn: [500],\n retryTimes: 2\n })\n }\n\n /**\n * Custom implementation of the rxjs retryWhen.\n * Simply use it in your pipe() method like:\n *\n * @example\n * obs$.pipe(\n * RxJS.retryWith({\n * delay: 500,\n * codesToRetryOn: [500],\n * retryTimes: 3\n * }),\n * catchError( err => {…})\n * )\n * @param options lets the developer specify:\n * - the maximum amount of times we can retry\n * - a base delay between retries\n * - an array of status codes where retrying on makes sense and\n * @returns the custom implementation of retryWhen with exponential backoff\n */\n public static retryWith(options: RetryOptions): MonoTypeOperatorFunction {\n // If unspecified, don't delay between retries\n const delay = options.delay ?? 0\n // If unspecified, don't match any statusCode\n const userSpecifiedRetryCodes = options.codesToRetryOn != null && options.codesToRetryOn.length > 0\n // If unspecified default to one retry .\n const maxRetryTimes = options.retryTimes ?? 1\n\n return retryWhen(errStream$ => errStream$.pipe(\n mergeMap(\n (error, emissionIndex) =>\n RxJS.decideIfCanRetry(\n error, emissionIndex, delay,\n maxRetryTimes, userSpecifiedRetryCodes,\n options.codesToRetryOn\n )\n )\n ))\n }\n\n /**\n * This pipe operator is intended to be used for Observables\n *\n * @param router: required to perform redirect.\n */\n public static redirectIfError500(router: Router): MonoTypeOperatorFunction {\n return tap(err => {\n if (parseInt(err?.status, 10) === 500) {\n RedirectTo.internalServerError(router, err)\n }\n })\n }\n\n /**\n * This pipe operator is intended to be used for Observables\n *\n * @param router: required to get the URL where the error occured.\n */\n public static redirectIfTimeout(router: Router): MonoTypeOperatorFunction {\n return tap(err => {\n if (parseInt(err?.status, 10) === 498) {\n RedirectTo.sessionTimeout(router, {\n state: {\n errorCode: ReponseErrors.SESSION_TIMEOUT,\n url: router.url\n }\n })\n }\n })\n }\n\n\n\n private static decideIfCanRetry(\n error: any,\n emissionIndex: number,\n delay: number,\n maxRetryTimes: number,\n userSpecifiedRetryCodes: boolean,\n codesToRetryOn: number[]\n ): Observable {\n const currentRetry = emissionIndex + 1 // indexes start at 0\n const exponentialBackoffDelay = currentRetry * delay\n const weMaxedOutRetries = currentRetry > maxRetryTimes\n // We've maxed out our retries; throw the error no matter what!\n if (weMaxedOutRetries) {\n return throwError(() => error)\n }\n // We still have retries; we should check the error status code if\n // 1. the developer specified status codes AND it's an http error.\n const shouldCheckStatusCode = userSpecifiedRetryCodes && error instanceof HttpErrorResponse\n if (shouldCheckStatusCode) {\n const httpErr = error as HttpErrorResponse\n // We can retry, if the current error code is inside the passed list\n const canRetryOnCurrentHttpCode = codesToRetryOn?.includes(httpErr.status) ?? false\n if (canRetryOnCurrentHttpCode) {\n return timer(exponentialBackoffDelay)\n } else {\n return throwError(() => error)\n }\n }\n // The error wasn't an HTTP error, we're gonna retry after the delay\n return timer(exponentialBackoffDelay)\n }\n}\n"], "mappings": ";;;;;;;;;;;;;;;;AAyBM,IAAO,OAAP,MAAO,MAAI;EAER,OAAO,kBACZ,YACA,eAAyB,MAAI;AAE7B,UAAM,WAAW,IAAI,gBAA0B,YAAY;AAE3D,UAAM,eAAe,WAAW,UAC9B,CAAC,UAAoB,SAAS,KAAK,KAAK,GACxC,WAAS,SAAS,MAAM,KAAK,GAC7B,MAAK;AACH,mBAAa,YAAW;AACxB,eAAS,SAAQ;IACnB,CAAC;AAGH,WAAO;EACT;EAEO,OAAO,UAAa,YAAyB;AAClD,UAAM,UAAU,IAAI,QAAO;AAE3B,UAAM,eAAe,WAAW,UAC9B,CAAC,UAAa,QAAQ,KAAK,KAAK,GAChC,WAAS,QAAQ,MAAM,KAAK,GAC5B,MAAK;AACH,mBAAa,YAAW;AACxB,cAAQ,SAAQ;IAClB,CAAC;AAEH,WAAO;EACT;EAEO,OAAO,cACwB,QAAc;AAChD,WAAO,WAAW,WAAQ;AACxB,aAAO,MAAM,KAAK;AAClB,aAAO,WAAW,MAAM,KAAK;IAC/B,CAAC;EACH;;;;;EAMK,OAAO,kBACL,cAAe;AACpB,WAAO,WAA+B,CAAC,UAAS;AAC9C,UAAI,OAAO,WAAW,KAAK;AAAE,eAAO,GAAG,YAAY;MAAE;AACrD,aAAO,WAAW,MAAM,KAAK;IAC/B,CAAC;EACH;;;;;EAMK,OAAO,kBACP,cAAe;AACpB,WAAO,WAA+B,CAAC,UAAS;AAC9C,UAAI,OAAO,WAAW,KAAK;AAAE,eAAO,GAAG,YAAY;MAAE;AACrD,aAAO,WAAW,MAAM,KAAK;IAC/B,CAAC;EACH;;;;;;;EASO,OAAO,oBAAiB;AAC7B,WAAO,MAAK,UAAU;MACpB,OAAO;MACP,gBAAgB,CAAC,GAAG;MACpB,YAAY;KACb;EACH;;;;;;;;;;;;;;;;;;;;EAqBO,OAAO,UAAa,SAAqB;AAE9C,UAAM,QAAQ,QAAQ,SAAS;AAE/B,UAAM,0BAA0B,QAAQ,kBAAkB,QAAQ,QAAQ,eAAe,SAAS;AAElG,UAAM,gBAAgB,QAAQ,cAAc;AAE5C,WAAO,UAAa,gBAAc,WAAW,KAC3C,SACE,CAAC,OAAO,kBACN,MAAK,iBACH,OAAO,eAAe,OACtB,eAAe,yBACf,QAAQ,cAAc,CACvB,CACJ,CACF;EACH;;;;;;EAOO,OAAO,mBAAmB,QAAc;AAC7C,WAAO,IAAS,SAAM;AACpB,UAAI,SAAS,KAAK,QAAQ,EAAE,MAAM,KAAK;AACrC,mBAAW,oBAAoB,QAAQ,GAAG;MAC5C;IACF,CAAC;EACH;;;;;;EAOO,OAAO,kBAAkB,QAAc;AAC5C,WAAO,IAAS,SAAM;AACpB,UAAI,SAAS,KAAK,QAAQ,EAAE,MAAM,KAAK;AACrC,mBAAW,eAAe,QAAQ;UAChC,OAAO;YACL,WAAW,cAAc;YACzB,KAAK,OAAO;;SAEf;MACH;IACF,CAAC;EACH;EAIQ,OAAO,iBACb,OACA,eACA,OACA,eACA,yBACA,gBAAwB;AAExB,UAAM,eAAe,gBAAgB;AACrC,UAAM,0BAA0B,eAAe;AAC/C,UAAM,oBAAoB,eAAe;AAEzC,QAAI,mBAAmB;AACrB,aAAO,WAAW,MAAM,KAAK;IAC/B;AAGA,UAAM,wBAAwB,2BAA2B,iBAAiB;AAC1E,QAAI,uBAAuB;AACzB,YAAM,UAAU;AAEhB,YAAM,4BAA4B,gBAAgB,SAAS,QAAQ,MAAM,KAAK;AAC9E,UAAI,2BAA2B;AAC7B,eAAO,MAAM,uBAAuB;MACtC,OAAO;AACL,eAAO,WAAW,MAAM,KAAK;MAC/B;IACF;AAEA,WAAO,MAAM,uBAAuB;EACtC;;", "names": [] }