From b7f644036d8fbb9bf358aff970825b6906184da6 Mon Sep 17 00:00:00 2001 From: Adam Herrmann Date: Thu, 26 Apr 2018 15:06:05 -0400 Subject: [PATCH 1/2] Allow specific CacheMap key type --- src/index.d.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/index.d.ts b/src/index.d.ts index aa40aac..7a5ca66 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -15,9 +15,9 @@ * with different access permissions and consider creating a new instance * per web request. */ -declare class DataLoader { +declare class DataLoader { - constructor(batchLoadFn: DataLoader.BatchLoadFn, options?: DataLoader.Options); + constructor(batchLoadFn: DataLoader.BatchLoadFn, options?: DataLoader.Options); /** * Loads a key, returning a `Promise` for the value represented by that key. @@ -61,10 +61,10 @@ declare class DataLoader { declare namespace DataLoader { // If a custom cache is provided, it must be of this type (a subset of ES6 Map). - export type CacheMap = { - get(key: K): V | void; - set(key: K, value: V): any; - delete(key: K): any; + export type CacheMap = { + get(key: C): V | void; + set(key: C, value: V): any; + delete(key: C): any; clear(): any; } @@ -74,7 +74,7 @@ declare namespace DataLoader { // Optionally turn off batching or caching or provide a cache key function or a // custom cache instance. - export type Options = { + export type Options = { /** * Default `true`. Set to `false` to disable batching, @@ -102,14 +102,14 @@ declare namespace DataLoader { * objects are keys and two similarly shaped objects should * be considered equivalent. */ - cacheKeyFn?: (key: any) => any, + cacheKeyFn?: (key: K) => C, /** * An instance of Map (or an object with a similar API) to * be used as the underlying cache for this loader. * Default `new Map()`. */ - cacheMap?: CacheMap>; + cacheMap?: CacheMap>; } } From 67799f3eb41612720b1701a13fbd702de5da7082 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 13 Nov 2019 10:01:46 -0800 Subject: [PATCH 2/2] Update impl, flow types, and tests --- src/__tests__/dataloader.test.js | 7 +++-- src/index.d.ts | 14 ++++----- src/index.js | 50 ++++++++++++++++++-------------- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/__tests__/dataloader.test.js b/src/__tests__/dataloader.test.js index 5327a43..ddd2af8 100644 --- a/src/__tests__/dataloader.test.js +++ b/src/__tests__/dataloader.test.js @@ -7,9 +7,12 @@ * @flow */ +import type { Options } from '..'; const DataLoader = require('..'); -function idLoader(options) { +function idLoader( + options?: Options +): [ DataLoader, Array<$ReadOnlyArray> ] { const loadCalls = []; const identityLoader = new DataLoader(keys => { loadCalls.push(keys); @@ -568,7 +571,7 @@ describe('Accepts options', () => { }); describe('Accepts object key in custom cacheKey function', () => { - function cacheKey(key) { + function cacheKey(key: {[string]: any}): string { return Object.keys(key).sort().map(k => k + ':' + key[k]).join(); } diff --git a/src/index.d.ts b/src/index.d.ts index 7a5ca66..9224b55 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -43,28 +43,28 @@ declare class DataLoader { * Clears the value at `key` from the cache, if it exists. Returns itself for * method chaining. */ - clear(key: K): DataLoader; + clear(key: K): this; /** * Clears the entire cache. To be used when some event results in unknown * invalidations across this particular `DataLoader`. Returns itself for * method chaining. */ - clearAll(): DataLoader; + clearAll(): this; /** * Adds the provied key and value to the cache. If the key already exists, no * change is made. Returns itself for method chaining. */ - prime(key: K, value: V): DataLoader; + prime(key: K, value: V): this; } declare namespace DataLoader { // If a custom cache is provided, it must be of this type (a subset of ES6 Map). - export type CacheMap = { - get(key: C): V | void; - set(key: C, value: V): any; - delete(key: C): any; + export type CacheMap = { + get(key: K): V | void; + set(key: K, value: V): any; + delete(key: K): any; clear(): any; } diff --git a/src/index.js b/src/index.js index c38c983..4bcaff9 100644 --- a/src/index.js +++ b/src/index.js @@ -14,12 +14,12 @@ export type BatchLoadFn = // Optionally turn off batching or caching or provide a cache key function or a // custom cache instance. -export type Options = { +export type Options = { batch?: boolean; maxBatchSize?: number; cache?: boolean; - cacheKeyFn?: (key: any) => any; - cacheMap?: CacheMap>; + cacheKeyFn?: (key: K) => C; + cacheMap?: CacheMap>; }; // If a custom cache is provided, it must be of this type (a subset of ES6 Map). @@ -40,10 +40,10 @@ export type CacheMap = { * different access permissions and consider creating a new instance per * web request. */ -class DataLoader { +class DataLoader { constructor( batchLoadFn: BatchLoadFn, - options?: Options + options?: Options ) { if (typeof batchLoadFn !== 'function') { throw new TypeError( @@ -59,8 +59,8 @@ class DataLoader { // Private _batchLoadFn: BatchLoadFn; - _options: ?Options; - _promiseCache: CacheMap>; + _options: ?Options; + _promiseCache: CacheMap>; _queue: LoaderQueue; /** @@ -78,8 +78,7 @@ class DataLoader { var options = this._options; var shouldBatch = !options || options.batch !== false; var shouldCache = !options || options.cache !== false; - var cacheKeyFn = options && options.cacheKeyFn; - var cacheKey = cacheKeyFn ? cacheKeyFn(key) : key; + var cacheKey = getCacheKey(options, key); // If caching and there is a cache-hit, return cached Promise. if (shouldCache) { @@ -143,9 +142,8 @@ class DataLoader { * Clears the value at `key` from the cache, if it exists. Returns itself for * method chaining. */ - clear(key: K): DataLoader { - var cacheKeyFn = this._options && this._options.cacheKeyFn; - var cacheKey = cacheKeyFn ? cacheKeyFn(key) : key; + clear(key: K): this { + var cacheKey = getCacheKey(this._options, key); this._promiseCache.delete(cacheKey); return this; } @@ -155,7 +153,7 @@ class DataLoader { * invalidations across this particular `DataLoader`. Returns itself for * method chaining. */ - clearAll(): DataLoader { + clearAll(): this { this._promiseCache.clear(); return this; } @@ -164,9 +162,8 @@ class DataLoader { * Adds the provided key and value to the cache. If the key already * exists, no change is made. Returns itself for method chaining. */ - prime(key: K, value: V): DataLoader { - var cacheKeyFn = this._options && this._options.cacheKeyFn; - var cacheKey = cacheKeyFn ? cacheKeyFn(key) : key; + prime(key: K, value: V): this { + var cacheKey = getCacheKey(this._options, key); // Only add the key if it does not already exist. if (this._promiseCache.get(cacheKey) === undefined) { @@ -224,7 +221,7 @@ var resolvedPromise; // Private: given the current state of a Loader instance, perform a batch load // from its current queue. -function dispatchQueue(loader: DataLoader) { +function dispatchQueue(loader: DataLoader) { // Take the current loader queue, replacing it with an empty queue. var queue = loader._queue; loader._queue = []; @@ -245,7 +242,7 @@ function dispatchQueue(loader: DataLoader) { } function dispatchQueueBatch( - loader: DataLoader, + loader: DataLoader, queue: LoaderQueue ) { // Collect all keys to be loaded in this dispatch @@ -303,7 +300,7 @@ function dispatchQueueBatch( // Private: do not cache individual loads if the entire batch dispatch fails, // but still reject each request so they do not hang. function failedDispatch( - loader: DataLoader, + loader: DataLoader, queue: LoaderQueue, error: Error ) { @@ -313,10 +310,19 @@ function failedDispatch( }); } +// Private: produce a cache key for a given key (and options) +function getCacheKey( + options: ?Options, + key: K +): C { + var cacheKeyFn = options && options.cacheKeyFn; + return cacheKeyFn ? cacheKeyFn(key) : (key: any); +} + // Private: given the DataLoader's options, produce a CacheMap to be used. -function getValidCacheMap( - options: ?Options -): CacheMap> { +function getValidCacheMap( + options: ?Options +): CacheMap> { var cacheMap = options && options.cacheMap; if (!cacheMap) { return new Map();