Skip to content

Commit

Permalink
refactor(serviceConfig): memoize transformers (#1272)
Browse files Browse the repository at this point in the history
* helpers, memoClone for passed callback parameters
* serviceConfig, callback memoClone, error messaging
  • Loading branch information
cdcabrera committed Feb 19, 2024
1 parent 03a06fa commit 50d11ad
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 9 deletions.
20 changes: 20 additions & 0 deletions src/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ Download the debug log file.
## General

* [General](#Helpers.module_General)
* [~memoClone](#Helpers.module_General..memoClone) ⇒ <code>any</code>
* [~noop](#Helpers.module_General..noop)
* [~noopPromise](#Helpers.module_General..noopPromise) : <code>Promise.&lt;{}&gt;</code>
* [~setImmutableData](#Helpers.module_General..setImmutableData) ⇒ <code>\*</code>
Expand Down Expand Up @@ -315,6 +316,25 @@ Download the debug log file.
* [~objFreeze(obj)](#Helpers.module_General..objFreeze) ⇒ <code>\*</code>
* [~browserExpose(obj, options)](#Helpers.module_General..browserExpose)

<a name="Helpers.module_General..memoClone"></a>

### General~memoClone ⇒ <code>any</code>
Used for the numerous configuration callbacks that drive the UI. Often the same data
is passed repeatedly. Avoid "accidental" mutation.

**Kind**: inner constant of [<code>General</code>](#Helpers.module_General)
<table>
<thead>
<tr>
<th>Param</th><th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>value</td><td><code>any</code></td>
</tr> </tbody>
</table>

<a name="Helpers.module_General..noop"></a>

### General~noop
Expand Down
3 changes: 3 additions & 0 deletions src/common/__tests__/__snapshots__/helpers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ exports[`Helpers should expose a window object: limited window object 1`] = `
"isPromise": [Function],
"lorem": "ipsum",
"memo": [Function],
"memoClone": [Function],
"noop": [Function],
"noopPromise": Promise {},
"numberDisplay": [Function],
Expand Down Expand Up @@ -90,6 +91,7 @@ exports[`Helpers should expose a window object: window object 1`] = `
"isPromise": [Function],
"lorem": "ipsum",
"memo": [Function],
"memoClone": [Function],
"noop": [Function],
"noopPromise": Promise {},
"numberDisplay": [Function],
Expand Down Expand Up @@ -147,6 +149,7 @@ exports[`Helpers should have specific functions: helpers 1`] = `
"isDate": [Function],
"isPromise": [Function],
"memo": [Function],
"memoClone": [Function],
"noop": [Function],
"noopPromise": Promise {},
"numberDisplay": [Function],
Expand Down
10 changes: 10 additions & 0 deletions src/common/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ const memo = (func, { cacheLimit = 1 } = {}) => {
return ized();
};

/**
* Used for the numerous configuration callbacks that drive the UI. Often the same data
* is passed repeatedly. Avoid "accidental" mutation.
*
* @param {any} value
* @returns {any}
*/
const memoClone = memo(value => _cloneDeep(value), { cacheLimit: 25 });

/**
* An empty function.
* Typically used as a default prop.
Expand Down Expand Up @@ -444,6 +453,7 @@ const helpers = {
isDate,
isPromise,
memo,
memoClone,
noop,
noopPromise,
numberDisplay,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ exports[`Service Helpers should have specific functions: serviceHelpers 1`] = `
{
"camelCase": [Function],
"generateHash": [Function],
"memoClone": [Function],
"passDataToCallback": [Function],
"schemaResponse": [Function],
"timeoutFunctionCancel": [Function],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,9 @@ exports[`ServiceConfig should handle transforming service call responses: transf
[
"success-schema-transform",
"success-transform",
"success",
"error-error-transform",
"error",
[
"cancelled request",
undefined,
Expand Down
28 changes: 27 additions & 1 deletion src/services/common/__tests__/serviceConfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ describe('ServiceConfig', () => {
});
responses.push(responseTwo.data);

// Second-Error, use a transform but expect an error
const responseTwoError = await serviceConfig.axiosServiceCall({
cache: true,
url: '/test/',
transform: [
() => {
throw new Error('success response transform error');
}
]
});
responses.push(responseTwoError.data);

// Third, use error transform
const responseThree = await returnPromiseAsync(async () =>
serviceConfig.axiosServiceCall({
Expand All @@ -199,9 +211,23 @@ describe('ServiceConfig', () => {
]
})
);

responses.push(responseThree.data);

// Third-Error, use error transform
const responseThreeError = await returnPromiseAsync(async () =>
serviceConfig.axiosServiceCall({
cache: true,
url: '/error/',
transform: [
successResponse => `${successResponse}-transform`,
() => {
throw new Error('error response transform error');
}
]
})
);
responses.push(responseThreeError.data);

// Fourth, use error transform with cancel
const responseFourConfig = {
cache: true,
Expand Down
6 changes: 4 additions & 2 deletions src/services/common/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { helpers } from '../../common';
*/

/**
* Pass through generate hash
* Pass through generate hash, memo clone
*/
const { generateHash } = helpers;
const { generateHash, memoClone } = helpers;

/**
* A timeout cancel for function calls.
Expand Down Expand Up @@ -122,6 +122,7 @@ const schemaResponse = ({ casing, convert = true, id = null, response, schema }
const serviceHelpers = {
camelCase,
generateHash,
memoClone,
passDataToCallback,
schemaResponse,
timeoutFunctionCancel
Expand All @@ -132,6 +133,7 @@ export {
serviceHelpers,
camelCase,
generateHash,
memoClone,
passDataToCallback,
schemaResponse,
timeoutFunctionCancel
Expand Down
23 changes: 17 additions & 6 deletions src/services/common/serviceConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const axiosServiceCall = async (
updatedConfig.cacheId = cacheId;
}

// apply cancel configuration
if (updatedConfig.cancel === true) {
const cancelTokensId =
updatedConfig.cancelId || serviceHelpers.generateHash({ ...updatedConfig, data: undefined, params: undefined });
Expand All @@ -96,6 +97,7 @@ const axiosServiceCall = async (
delete updatedConfig.cancel;
}

// if cached response return
if (updatedConfig.cacheResponse === true) {
const cachedResponse = responseCache.get(cacheId);

Expand All @@ -112,14 +114,17 @@ const axiosServiceCall = async (
}
}

// if schema transform, add before standard transform
if (updatedConfig.schema) {
responseTransformers.push(updatedConfig.schema);
}

// add response transformers
if (updatedConfig.transform) {
responseTransformers.push(updatedConfig.transform);
}

// apply response transformers
responseTransformers.forEach(([successTransform, errorTransform]) => {
const transformers = [undefined, response => Promise.reject(response)];

Expand All @@ -128,11 +133,13 @@ const axiosServiceCall = async (
const updatedResponse = { ...response };
const { data, error: normalizeError } = serviceHelpers.passDataToCallback(
successTransform,
updatedResponse.data,
updatedResponse.config
serviceHelpers.memoClone(updatedResponse.data),
serviceHelpers.memoClone(updatedResponse.config)
);

if (!normalizeError) {
if (normalizeError) {
console.warn(normalizeError);
} else {
updatedResponse.data = data;
}

Expand All @@ -150,11 +157,13 @@ const axiosServiceCall = async (

const { data, error: normalizeError } = serviceHelpers.passDataToCallback(
errorTransform,
updatedResponse?.data || updatedResponse?.message,
updatedResponse.config
serviceHelpers.memoClone(updatedResponse?.data || updatedResponse?.message),
serviceHelpers.memoClone(updatedResponse.config)
);

if (!normalizeError) {
if (normalizeError) {
console.warn(normalizeError);
} else {
updatedResponse.response = { ...updatedResponse, data };
}

Expand All @@ -165,6 +174,7 @@ const axiosServiceCall = async (
axiosInstance.interceptors.response.use(...transformers);
});

// apply a response to cache
if (updatedConfig.cacheResponse === true) {
axiosInstance.interceptors.response.use(
response => {
Expand All @@ -176,6 +186,7 @@ const axiosServiceCall = async (
);
}

// use a function instead of a url-string, receive service emulated output (for implementation consistency)
if (typeof updatedConfig.url === 'function') {
const emulateCallback = updatedConfig.url;
updatedConfig.url = '/emulated';
Expand Down
2 changes: 2 additions & 0 deletions tests/__snapshots__/code.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ exports[`General code checks should only have specific console.[warn|log|info|er
"redux/common/reduxHelpers.js:287: console.error(\`Error: Property \${prop} does not exist within the passed state.\`, state);",
"redux/common/reduxHelpers.js:291: console.warn(\`Warning: Property \${prop} does not exist within the passed initialState.\`, initialState);",
"services/common/helpers.js:105: console.error(",
"services/common/serviceConfig.js:141: console.warn(normalizeError);",
"services/common/serviceConfig.js:165: console.warn(normalizeError);",
]
`;

0 comments on commit 50d11ad

Please sign in to comment.