All files / service resilient-http-client.service.ts

100% Statements 30/30
100% Branches 4/4
100% Functions 8/8
100% Lines 28/28

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 721x 1x 1x 1x 1x     1x         1x 21x     21x       21x 21x     7x   21x   21x   21x             7x   7x   4x       10x   10x 2x 1x     9x     21x 19x   19x             21x 21x          
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError, finalize, map, Observable, of, take, tap, throwError, timer } from 'rxjs';
import { applyResilience, shouldLogResult, shouldTrace } from '../util/rx/resilience.rx-operator';
import { v4 as uuidv4 } from 'uuid';
import { THttpGetOption } from '../model/type/http-get-options.type';
import { IResilienceConfig } from '../model/type/resilience.rx-operator.type';
import { DEFAULT_RESILIENCE_CONFIG } from '../util/rx/resilience.rx-operator.config';
 
@Injectable({
    providedIn: 'root',
})
export class ResilientHttpClientService {
    constructor(private readonly httpClient: HttpClient) {}
 
    public get<T>(url: string, resilienceConfig: Partial<IResilienceConfig>, options?: THttpGetOption): Observable<T> {
        const finalConfig = {
            ...DEFAULT_RESILIENCE_CONFIG,
            ...resilienceConfig,
        } as IResilienceConfig;
        const uuid = uuidv4();
        const isDelayedSubscription = timer(finalConfig.isDelayedAfterMs)
            .pipe(take(1))
            .subscribe(() => {
                finalConfig.onRequestDelayed(finalConfig.topic, uuid);
            });
        const timeStart = Date.now();
 
        finalConfig.onRequestStart(finalConfig.topic, uuid);
 
        return this.httpClient
            .get<T>(url, {
                ...options,
                observe: 'response',
            })
            .pipe(
                applyResilience(finalConfig, uuid),
                map((res: HttpResponse<T>) => res.body as T),
                tap((res) => {
                    if (shouldLogResult(finalConfig)) {
                        // eslint-disable-next-line no-console
                        console.log(finalConfig.topic, res);
                    }
                }),
                catchError((err) => {
                    const topicRelatedConfig = finalConfig.topicToConfigDict[finalConfig.topic];
 
                    if (topicRelatedConfig) {
                        if (topicRelatedConfig.failoverResponse) {
                            return of(topicRelatedConfig.failoverResponse as T);
                        }
                    }
                    return throwError(() => err);
                }),
                finalize(() => {
                    if (shouldTrace(finalConfig)) {
                        const timeEnd = Date.now();
                        // eslint-disable-next-line no-console
                        console.log(
                            `Fetch time in ms of call-topic ${finalConfig.topic}:`,
                            finalConfig.topic,
                            uuid,
                            timeEnd - timeStart,
                        );
                    }
                    finalConfig.onRequestFinalize(finalConfig.topic, uuid);
                    isDelayedSubscription.unsubscribe();
                }),
            );
    }
}