Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions packages/cache/src/cache.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Platform } from '@angular/cdk/platform';
import { HttpClient } from '@angular/common/http';
import { Injectable, OnDestroy, inject } from '@angular/core';
import { BehaviorSubject, Observable, of, map, tap } from 'rxjs';
import { BehaviorSubject, Observable, of, tap, from, switchMap, map } from 'rxjs';

import { addSeconds } from 'date-fns';

Expand Down Expand Up @@ -52,8 +52,9 @@ export class CacheService implements OnDestroy {
this.saveMeta();
}

private loadMeta(): void {
const ret = this.store.get(this.cog.meta_key!);
private async loadMeta(): Promise<void> {
const fn = this.store.get(this.cog.meta_key!);
const ret = fn instanceof Promise ? await fn : fn;
if (ret && ret.v) {
(ret.v as string[]).forEach(key => this.meta.add(key));
}
Expand Down Expand Up @@ -205,28 +206,36 @@ export class CacheService implements OnDestroy {
): Observable<any> | any {
if (!this.platform.isBrowser) return null;

const ret = this.memory.has(key) ? (this.memory.get(key) as ICache) : this.store.get(this.cog.prefix + key);
const fnIsPromise = ret instanceof Promise;
const isValid = (value?: ICache): boolean =>
value != null && (value.e == 0 || (value.e > 0 && value.e > new Date().valueOf()));
const isPromise = options.mode !== 'none' && this.cog.mode === 'promise';
const value = this.memory.has(key) ? (this.memory.get(key) as ICache) : this.store.get(this.cog.prefix + key);
if (!value || (value.e && value.e > 0 && value.e < new Date().valueOf())) {
if (isPromise) {
return (this.cog.request ? this.cog.request(key) : this.http.get(key)).pipe(
map((ret: any) => deepGet(ret, this.cog.reName as string[], ret)),
tap(v =>
this.set(key, v, {
type: options.type as any,
expire: options.expire,
emitNotify: options.emitNotify
})
)
);
}
return null;
if (!isPromise) {
const retObj = ret as ICache;
return isValid(retObj) ? retObj?.v : null;
}

return isPromise ? of(value.v) : value.v;
return (fnIsPromise ? from(ret) : of(ret)).pipe(
switchMap((data: ICache) => {
if (isValid(data)) return of(data.v);
return this.cog.request ? this.cog.request(key) : this.http.get(key);
}),
map(data => {
const v = deepGet(data, this.cog.reName as string[], data);
this.set(key, v, {
type: options.type as any,
expire: options.expire,
emitNotify: options.emitNotify
});
return v;
})
);
}

/** 获取缓存数据,若 `key` 不存在或已过期则返回 null */
/**
* 获取缓存数据,若 `key` 不存在或已过期则返回 null
*/
getNone<T>(key: string): T;
/** 获取缓存数据,若 `key` 不存在或已过期则返回 null */
getNone(key: string): any {
Expand Down
35 changes: 30 additions & 5 deletions packages/cache/src/cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { firstValueFrom, Observable, of, filter } from 'rxjs';
import { AlainCacheConfig, provideAlainConfig } from '@delon/util/config';

import { CacheService } from './cache.service';
import { ICache } from './interface';
import { ICache, ICacheStore } from './interface';
import { DC_STORE_STORAGE_TOKEN } from './local-storage-cache.service';

describe('cache: service', () => {
let srv: CacheService;
Expand All @@ -34,13 +35,13 @@ describe('cache: service', () => {
});
});

function genModule(options?: AlainCacheConfig): void {
const providers: any[] = [provideHttpClient(), provideHttpClientTesting()];
function genModule(options?: AlainCacheConfig, providers?: any[]): void {
const p: any[] = [provideHttpClient(), provideHttpClientTesting(), ...(providers ?? [])];
if (options) {
providers.push(provideAlainConfig({ cache: options }));
p.push(provideAlainConfig({ cache: options }));
}
TestBed.configureTestingModule({
providers
providers: p
});

srv = TestBed.inject<CacheService>(CacheService);
Expand Down Expand Up @@ -353,4 +354,28 @@ describe('cache: service', () => {
expect(request).toHaveBeenCalled();
expect(res).toBe(returnValue);
});

describe('cache store support async', () => {
it('should be working', () => {
const mockDict: Record<string, ICache> = {};
class MockStore implements ICacheStore {
get(key: string): ICache | PromiseLike<ICache> | null {
return Promise.resolve(mockDict[key]);
}
set(key: string, value: ICache): boolean | PromiseLike<boolean> {
mockDict[key] = value;
return Promise.resolve(true);
}
remove(key: string): void {
delete mockDict[key];
}
}
genModule(undefined, [{ provide: DC_STORE_STORAGE_TOKEN, useClass: MockStore, multi: false }]);
srv.set('a', 123, { type: 's' });
expect(mockDict['a']).not.toBeNull();
srv.get('a').subscribe(res => {
expect(res).toBe(123);
});
});
});
});
4 changes: 2 additions & 2 deletions packages/cache/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ export interface ICache {
}

export interface ICacheStore {
get(key: string): ICache | null;
get(key: string): ICache | PromiseLike<ICache> | null;

set(key: string, value: ICache): boolean;
set(key: string, value: ICache): boolean | PromiseLike<boolean>;

remove(key: string): void;
}
Expand Down
Loading