diff --git a/README.md b/README.md index 77cc41d..247b8b4 100644 --- a/README.md +++ b/README.md @@ -597,20 +597,17 @@ To overcome this, you can specify a function instead of a `headers` object in th This function will be called, to re-make the headers just before an API call is made, even when you call `run`. -Note: If you don't want the dynamic headers to result in a cache miss, you must remove the keys of the dynamic headers from the options using `mapOptionsToKey` (see example below). +Note: If you don't want the dynamic headers to result in a cache miss, you must remove the keys of the dynamic headers from the options using `mapOptionsToKey` (see example below that uses the `ignoreHeadersByKey` helper). ```jsx import React from 'react'; -import { useFetchye } from 'fetchye'; +import { useFetchye, ignoreHeadersByKey } from 'fetchye'; import uuid from 'uuid'; const BookList = () => { const { isLoading, data } = useFetchye('http://example.com/api/books/', { // remove the 'correlationId' header from the headers, as its the only dynamic header - mapOptionsToKey: ({ headers: { correlationId, ...headers }, ...options }) => ({ - ...options, - headers, - }), + mapOptionsToKey: ignoreHeadersByKey(['correlationId']), headers: () => ({ // static headers are still fine, and can be specified here like normal staticHeader: 'staticValue', @@ -810,6 +807,8 @@ const ParentComponent = ({ children }) => ( * [`DELETE_DATA`](#delete_data) * [`ERROR`](#error) * [`CLEAR_ERROR`](#clear_error) +* [`mapOptionToKey Helpers`](#mapoptiontokey_helpers) + * [`ignoreHeadersByKey`](#ignoreheadersbykey) ### `useFetchye` @@ -833,7 +832,7 @@ const { isLoading, data, error, run } = useFetchye(key, { defer: Boolean, mapOpt | name | type | required | description | |--------------------|-------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `mapOptionsToKey` | `(options: Options) => transformedOptions` | `false` | A function that maps options to the key that will become part of the cache key | +| `mapOptionsToKey` | `(options: Options) => transformedOptions` | `false` | A function that maps options to the key that will become part of the cache key. See below for a list of mapOptionsToKey helpers. | | `mapKeyToCacheKey` | `(key: String, options: Options) => cacheKey: String` | `false` | A function that maps the key for use as the cacheKey allowing direct control of the cacheKey | | `defer` | `Boolean` | `false` | Prevents execution of `useFetchye` on each render in favor of using the returned `run` function. *Defaults to `false`* | | `initialData` | `Object` | `false` | Seeds the initial data on first render of `useFetchye` to accomodate server side rendering *Defaults to `undefined`* | @@ -1221,6 +1220,52 @@ import { CLEAR_ERROR } from 'fetchye-core'; |---|---|---| | `hash` | `String` | The hash value generated by [`object-hash`](https://github.com/puleos/object-hash) package for the query | +### mapOptionToKey Helpers +These helpers provide a more compact and simple way of common transforms. + +There is currently one helper. + +#### ignoreHeadersByKey + +**Shape** + +`(String[]) => mapOptionsToKeyFunction` + +**Arguments** + +| name | type | required | description | +|--------|-----------------|----------|--------------------------------------------------------------------------------------------------| +| `keys` | `Array` | `true` | creates a mapOptionsToKey function that removed headers whos keys that match the specified keys | + +**Returns** + +| name | type | description | +|----------------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------------| +| `mapOptionsToKey Function` | `mapOptionsToKeyFunction` | A function to be passed to the mapOptionsToKey option in useFetchye, or the `fetchye` function made by `makeServerFetchye`. | + +**Example** + +```jsx +import React from 'react'; +import { useFetchye, ignoreHeadersByKey } from 'fetchye'; + +const BookList = ({ locale }) => { + const { isLoading, data } = useFetchye('http://example.com/api/books/', { + // remove the 'locale' header from the headers when building the cache key + mapOptionsToKey: ignoreHeadersByKey(['locale']), + headers: { locale }, + }); + + // ... rest of component +}; + +export default BookList; +``` + + + + + ## 📢 Mission The Fetchye project wishes to bring a more flexible central caching experience diff --git a/packages/fetchye/__tests__/index.spec.js b/packages/fetchye/__tests__/index.spec.js index ec78fbe..331eb5c 100644 --- a/packages/fetchye/__tests__/index.spec.js +++ b/packages/fetchye/__tests__/index.spec.js @@ -22,6 +22,7 @@ describe('index', () => { Array [ "FetchyeProvider", "SimpleCache", + "ignoreHeadersByKey", "makeServerFetchye", "useFetchye", ] diff --git a/packages/fetchye/__tests__/mapOptionsToKeyHelpers.spec.js b/packages/fetchye/__tests__/mapOptionsToKeyHelpers.spec.js new file mode 100644 index 0000000..6535987 --- /dev/null +++ b/packages/fetchye/__tests__/mapOptionsToKeyHelpers.spec.js @@ -0,0 +1,54 @@ +/* + * Copyright 2023 American Express Travel Related Services Company, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { ignoreHeadersByKey } from '../src/mapOptionsToKeyHelpers'; + +describe('mapOptionsToKeyHelpers', () => { + describe('ignoreHeadersByKey', () => { + it('should do nothing if the header key is not present', () => { + expect(ignoreHeadersByKey([])({})).toEqual({}); + expect(ignoreHeadersByKey([])( + { otherKey: 'otherKeyValue' } + )).toEqual( + { otherKey: 'otherKeyValue' } + ); + }); + it('should do nothing if the header key is present, but the mentioned keys to remove are not present', () => { + expect(ignoreHeadersByKey(['removeHeaderKey'])( + { headers: { preserveHeaderKey: 'preserveHeaderKeyValue' } } + )).toEqual( + { headers: { preserveHeaderKey: 'preserveHeaderKeyValue' } } + ); + expect(ignoreHeadersByKey(['removeHeaderKey'])( + { otherKey: 'otherKeyValue', headers: { preserveHeaderKey: 'preserveHeaderKeyValue' } } + )).toEqual( + { otherKey: 'otherKeyValue', headers: { preserveHeaderKey: 'preserveHeaderKeyValue' } } + ); + }); + it('should remove the mentioned key if its present in the headers', () => { + expect(ignoreHeadersByKey(['removeHeaderKey'])( + { headers: { preserveHeaderKey: 'preserveHeaderKeyValue', removeHeaderKey: 'removeHeaderKeyValue' } } + )).toEqual( + { headers: { preserveHeaderKey: 'preserveHeaderKeyValue' } } + ); + expect(ignoreHeadersByKey(['removeHeaderKey'])( + { otherKey: 'otherKeyValue', headers: { preserveHeaderKey: 'preserveHeaderKeyValue', removeHeaderKey: 'removeHeaderKeyValue' } } + )).toEqual( + { otherKey: 'otherKeyValue', headers: { preserveHeaderKey: 'preserveHeaderKeyValue' } } + ); + }); + }); +}); diff --git a/packages/fetchye/src/index.js b/packages/fetchye/src/index.js index a8bb1ef..ae187f6 100644 --- a/packages/fetchye/src/index.js +++ b/packages/fetchye/src/index.js @@ -18,10 +18,12 @@ import useFetchye from './useFetchye'; import makeServerFetchye from './makeServerFetchye'; import FetchyeProvider from './FetchyeProvider'; import SimpleCache from './SimpleCache'; +import { ignoreHeadersByKey } from './mapOptionsToKeyHelpers'; export { useFetchye, makeServerFetchye, FetchyeProvider, SimpleCache, + ignoreHeadersByKey, }; diff --git a/packages/fetchye/src/mapOptionsToKeyHelpers.js b/packages/fetchye/src/mapOptionsToKeyHelpers.js new file mode 100644 index 0000000..c1df4b1 --- /dev/null +++ b/packages/fetchye/src/mapOptionsToKeyHelpers.js @@ -0,0 +1,32 @@ +/* + * Copyright 2023 American Express Travel Related Services Company, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +const filterObjectByKeys = (object, keys) => Object.keys(object) + .filter((key) => !keys.includes(key)) + .reduce((acc, key) => { + // mutating accumulator to prevent many, many object allocations + acc[key] = object[key]; + return acc; + }, {}); + +export const ignoreHeadersByKey = (keys) => ({ headers, ...options }) => ( + headers + ? { + ...options, + headers: filterObjectByKeys(headers, keys), + } + : { ...options } +);