diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a19af9..719a3d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## `4.0.20` + + * Добавлена возможность передавать функцию в options.cache. + * Добавлена передача params в getRetryStrategy. + ## `4.0.19` * Добавлена возможность использования пользовательских стратегий ретраев. diff --git a/docs/cache.md b/docs/cache.md index b3d24d9..c8fe366 100644 --- a/docs/cache.md +++ b/docs/cache.md @@ -45,6 +45,8 @@ const cache = new de.Cache(); Он хранит все просто в памяти, нет ограничений на количество записей. Так что для каких-то серьезных ситуаций лучше его не использовать, а использовать [descript-redis-cache](https://www.npmjs.com/package/descript-redis-cache). +`cache` также может быть функцией `({ params }) => Promise>`. Если функция выбросит ошибку, то блок продолжит выполнение без кэширования. + ## `options.key` Обязательный параметр `key` задает ключ хранения. diff --git a/docs/http_block.md b/docs/http_block.md index 1ccd583..fbf023e 100644 --- a/docs/http_block.md +++ b/docs/http_block.md @@ -441,7 +441,7 @@ block: { ```js block: { - getRetryStragety: ( { requestOptions, request } ) => { + getRetryStragety: ( { requestOptions, request, params } ) => { return new MyAwesomeRetryStrategy({ request, ... }); }, }, diff --git a/lib/block.ts b/lib/block.ts index 5ee8ade..f2dd2b5 100644 --- a/lib/block.ts +++ b/lib/block.ts @@ -523,8 +523,16 @@ abstract class BaseBlock< depsDomain?: DepsDomain; }): Promise { let result: BlockResult | undefined = undefined; + let cache; + + try { + cache = typeof this.options.cache === 'function' ? + await this.options.cache({ params }) : + this.options.cache; + } catch { + // Do nothing + } - const cache = this.options.cache; let key; const optionsKey = this.options.key; diff --git a/lib/httpBlock.ts b/lib/httpBlock.ts index f1c0bc6..27a3007 100644 --- a/lib/httpBlock.ts +++ b/lib/httpBlock.ts @@ -1,7 +1,7 @@ import Block from './block'; import { ERROR_ID, createError } from './error'; -import type { DescriptRequestOptions, BlockRequestOptions } from './request'; +import type { DescriptRequestOptions, BlockRequestOptions, GetRetryStrategyParams } from './request'; import request from './request'; import extend from './extend'; @@ -13,6 +13,7 @@ import type ContextClass from './context'; import type Cancel from './cancel'; import type DepsDomain from './depsDomain'; import type { LoggerInterface } from './logger'; +import { RetryStrategyInterface } from './retryStrategy'; // --------------------------------------------------------------------------------------------------------------- // @@ -79,7 +80,7 @@ export interface DescriptHttpBlockDescription< HTTPResult, > extends Pick< DescriptRequestOptions, - 'isError' | 'isRetryAllowed' | 'retryTimeout' | 'getRetryStrategy' + 'isError' | 'isRetryAllowed' | 'retryTimeout' > { // sync with EVALUABLE_PROPS agent?: DescriptHttpBlockDescriptionCallback; @@ -120,6 +121,9 @@ export interface DescriptHttpBlockDescription< parseBody?: (result: { body: DescriptHttpResult['body']; headers: DescriptHttpResult['headers'] }, context: Context) => HTTPResult; + getRetryStrategy?: (args: { + params: Params; + } & GetRetryStrategyParams) => RetryStrategyInterface; } const EVALUABLE_PROPS: Array = { params, context, deps }; + const blockGetRetryStrategy = block.getRetryStrategy; + const getRetryStrategy = typeof blockGetRetryStrategy === 'function' ? + (args: GetRetryStrategyParams) => blockGetRetryStrategy({ ...args, params }) : + undefined; + let options: DescriptRequestOptions = { isError: block.isError, isRetryAllowed: block.isRetryAllowed, retryTimeout: block.retryTimeout, - getRetryStrategy: block.getRetryStrategy, + getRetryStrategy, body: null, ...( EVALUABLE_PROPS.reduce((ret, prop) => { diff --git a/lib/types.ts b/lib/types.ts index 20aacc6..6fdbc95 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -195,7 +195,7 @@ export interface DescriptBlockOptions< deps: DescriptBlockDeps; }) => string); maxage?: number; - cache?: CacheInterface; + cache?: CacheInterface | ((args: { params: ParamsOut }) => Promise>); required?: boolean; diff --git a/package-lock.json b/package-lock.json index b4385d9..bc80320 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "descript", - "version": "4.0.19", + "version": "4.0.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "descript", - "version": "4.0.19", + "version": "4.0.20", "license": "MIT", "dependencies": { "@fengkx/zstd-napi": "^0.1.0" diff --git a/package.json b/package.json index 67d0093..4bfd0fd 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ }, "name": "descript", "description": "descript", - "version": "4.0.19", + "version": "4.0.20", "homepage": "https://github.com/descript-org/descript", "repository": { "type": "git", diff --git a/tests/options.cache.test.ts b/tests/options.cache.test.ts index cb351db..5d41e4d 100644 --- a/tests/options.cache.test.ts +++ b/tests/options.cache.test.ts @@ -324,4 +324,47 @@ describe('options.cache, options.key, options.maxage', () => { }).not.toThrow(); }); + it('cache can be a function', async() => { + const cache = new Cache(); + const cachedValue = 'cached'; + const params = { foo: 'bar' }; + + await cache.set({ key: 'KEY', value: cachedValue, maxage: 10000 }); + + const spy = vi.fn(() => 'result'); + const cacheFn = vi.fn(() => Promise.resolve(cache)); + const block = getResultBlock(spy, 50).extend({ + options: { + cache: cacheFn, + key: 'KEY', + maxage: 10000, + }, + }); + + const result = await de.run(block, { params }); + + expect(result).toBe(cachedValue); + expect(spy.mock.calls).toHaveLength(0); + expect(cacheFn).toHaveBeenCalledWith({ params }); + }); + + it('cache function throws, block executes without cache', async() => { + const blockValue = 'result'; + + const spy = vi.fn(() => blockValue); + const cacheFn = vi.fn(() => Promise.reject(de.error('cache error'))); + const block = getResultBlock(spy, 50).extend({ + options: { + cache: cacheFn, + key: 'KEY', + maxage: 10000, + }, + }); + + const result = await de.run(block); + + expect(result).toBe(blockValue); + expect(spy.mock.calls).toHaveLength(1); + }); + });