diff --git a/src/common/README.md b/src/common/README.md
index 682431904..c241e5b91 100644
--- a/src/common/README.md
+++ b/src/common/README.md
@@ -274,6 +274,7 @@ Download the debug log file.
## General
* [General](#Helpers.module_General)
+ * [~memoClone](#Helpers.module_General..memoClone) ⇒ any
* [~noop](#Helpers.module_General..noop)
* [~noopPromise](#Helpers.module_General..noopPromise) : Promise.<{}>
* [~setImmutableData](#Helpers.module_General..setImmutableData) ⇒ \*
@@ -315,6 +316,25 @@ Download the debug log file.
* [~objFreeze(obj)](#Helpers.module_General..objFreeze) ⇒ \*
* [~browserExpose(obj, options)](#Helpers.module_General..browserExpose)
+
+
+### General~memoClone ⇒ any
+Used for the numerous configuration callbacks that drive the UI. Often the same data
+is passed repeatedly. Avoid "accidental" mutation.
+
+**Kind**: inner constant of [General
](#Helpers.module_General)
+
+
+
+ Param | Type |
+
+
+
+
+ value | any |
+
+
+
### General~noop
diff --git a/src/common/__tests__/__snapshots__/helpers.test.js.snap b/src/common/__tests__/__snapshots__/helpers.test.js.snap
index f8f9787ad..80ca64cd0 100644
--- a/src/common/__tests__/__snapshots__/helpers.test.js.snap
+++ b/src/common/__tests__/__snapshots__/helpers.test.js.snap
@@ -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],
@@ -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],
@@ -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],
diff --git a/src/common/helpers.js b/src/common/helpers.js
index b818df64b..066d1ddfa 100644
--- a/src/common/helpers.js
+++ b/src/common/helpers.js
@@ -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.
@@ -444,6 +453,7 @@ const helpers = {
isDate,
isPromise,
memo,
+ memoClone,
noop,
noopPromise,
numberDisplay,
diff --git a/src/services/common/__tests__/__snapshots__/helpers.test.js.snap b/src/services/common/__tests__/__snapshots__/helpers.test.js.snap
index 2bb568d7d..1f4fe7b2b 100644
--- a/src/services/common/__tests__/__snapshots__/helpers.test.js.snap
+++ b/src/services/common/__tests__/__snapshots__/helpers.test.js.snap
@@ -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],
diff --git a/src/services/common/__tests__/__snapshots__/serviceConfig.test.js.snap b/src/services/common/__tests__/__snapshots__/serviceConfig.test.js.snap
index f1d6591d2..422bcdc48 100644
--- a/src/services/common/__tests__/__snapshots__/serviceConfig.test.js.snap
+++ b/src/services/common/__tests__/__snapshots__/serviceConfig.test.js.snap
@@ -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,
diff --git a/src/services/common/__tests__/serviceConfig.test.js b/src/services/common/__tests__/serviceConfig.test.js
index d6f319180..fd3f05fe9 100644
--- a/src/services/common/__tests__/serviceConfig.test.js
+++ b/src/services/common/__tests__/serviceConfig.test.js
@@ -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({
@@ -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,
diff --git a/src/services/common/helpers.js b/src/services/common/helpers.js
index f0a69d397..bf3001d2c 100644
--- a/src/services/common/helpers.js
+++ b/src/services/common/helpers.js
@@ -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.
@@ -122,6 +122,7 @@ const schemaResponse = ({ casing, convert = true, id = null, response, schema }
const serviceHelpers = {
camelCase,
generateHash,
+ memoClone,
passDataToCallback,
schemaResponse,
timeoutFunctionCancel
@@ -132,6 +133,7 @@ export {
serviceHelpers,
camelCase,
generateHash,
+ memoClone,
passDataToCallback,
schemaResponse,
timeoutFunctionCancel
diff --git a/src/services/common/serviceConfig.js b/src/services/common/serviceConfig.js
index b089e5641..43102a30a 100644
--- a/src/services/common/serviceConfig.js
+++ b/src/services/common/serviceConfig.js
@@ -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 });
@@ -96,6 +97,7 @@ const axiosServiceCall = async (
delete updatedConfig.cancel;
}
+ // if cached response return
if (updatedConfig.cacheResponse === true) {
const cachedResponse = responseCache.get(cacheId);
@@ -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)];
@@ -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;
}
@@ -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 };
}
@@ -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 => {
@@ -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';
diff --git a/tests/__snapshots__/code.test.js.snap b/tests/__snapshots__/code.test.js.snap
index 8380e2c2f..f8f3fa6df 100644
--- a/tests/__snapshots__/code.test.js.snap
+++ b/tests/__snapshots__/code.test.js.snap
@@ -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);",
]
`;