import { filter, take, distinctUntilChanged, first } from 'rxjs/operators'; import { Injectable } from '@angular/core'; import { MemoizedSelector, select, Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { ResponseCacheEntry } from './response-cache.reducer'; import { hasNoValue } from '../../shared/empty.util'; import { ResponseCacheRemoveAction, ResponseCacheAddAction } from './response-cache.actions'; import { RestResponse } from './response-cache.models'; import { coreSelector, CoreState } from '../core.reducers'; import { pathSelector } from '../shared/selectors'; function entryFromKeySelector(key: string): MemoizedSelector { return pathSelector(coreSelector, 'cache/response', key); } /** * A service to interact with the response cache */ @Injectable() export class ResponseCacheService { constructor( private store: Store ) { } add(key: string, response: RestResponse, msToLive: number): Observable { if (!this.has(key)) { this.store.dispatch(new ResponseCacheAddAction(key, response, new Date().getTime(), msToLive)); } return this.get(key); } /** * Get an observable of the response with the specified key * * @param key * the key of the response to get * @return Observable * an observable of the ResponseCacheEntry with the specified key */ get(key: string): Observable { return this.store.pipe( select(entryFromKeySelector(key)), filter((entry: ResponseCacheEntry) => this.isValid(entry)), distinctUntilChanged() ) } /** * Check whether the response with the specified key is cached * * @param key * the key of the response to check * @return boolean * true if the response with the specified key is cached, * false otherwise */ has(key: string): boolean { let result: boolean; this.store.pipe(select(entryFromKeySelector(key)), first() ).subscribe((entry: ResponseCacheEntry) => { result = this.isValid(entry); }); return result; } remove(key: string): void { if (this.has(key)) { this.store.dispatch(new ResponseCacheRemoveAction(key)); } } /** * Check whether a ResponseCacheEntry should still be cached * * @param entry * the entry to check * @return boolean * false if the entry is null, undefined, or its time to * live has been exceeded, true otherwise */ private isValid(entry: ResponseCacheEntry): boolean { if (hasNoValue(entry)) { return false; } else { const timeOutdated = entry.timeAdded + entry.msToLive; const isOutDated = new Date().getTime() > timeOutdated; if (isOutDated) { this.store.dispatch(new ResponseCacheRemoveAction(entry.key)); } return !isOutDated; } } }