From 88a61d7b21fb7f4fb325492e186769e9e2357e8d Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Wed, 25 Apr 2018 17:38:48 +0200 Subject: [PATCH 01/10] wip --- .../config/checkActionsConfig.test.js | 53 +++++++++++++++++++ jest.config.js | 6 +-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/__tests__/internals/config/checkActionsConfig.test.js b/__tests__/internals/config/checkActionsConfig.test.js index b67057b..c436cf9 100644 --- a/__tests__/internals/config/checkActionsConfig.test.js +++ b/__tests__/internals/config/checkActionsConfig.test.js @@ -114,6 +114,26 @@ describe('checkActionsConfig', () => { expect(() => checkActionsConfig(RESOURCE_NAME, validConfig2)).not.toThrow(); }); + test('invalid cacheHint', () => { + const invalidConfig = { + eat: { + ...VALID_ACTION_CONFIG_BASE, + cacheHint: '', + }, + }; + + expect(() => checkActionsConfig(RESOURCE_NAME, invalidConfig)).toThrow(); + + const validConfig = { + eat: { + ...VALID_ACTION_CONFIG_BASE, + cacheHint: () => ({ cache: 'hint' }), + }, + }; + + expect(() => checkActionsConfig(RESOURCE_NAME, validConfig)).not.toThrow(); + }); + test('invalid beforeHook', () => { const invalidConfig = { eat: { @@ -193,4 +213,37 @@ describe('checkActionsConfig', () => { expect(() => checkActionsConfig(RESOURCE_NAME, validConfig)).not.toThrow(); }); + + test('invalid networkHelpers', () => { + const invalidConfig1 = { + eat: { + ...VALID_ACTION_CONFIG_BASE, + networkHelpers: '', + }, + }; + + expect(() => checkActionsConfig(RESOURCE_NAME, invalidConfig1)).toThrow(); + + const invalidConfig2 = { + eat: { + ...VALID_ACTION_CONFIG_BASE, + networkHelpers: { + getToken: 'customToken', + }, + }, + }; + + expect(() => checkActionsConfig(RESOURCE_NAME, invalidConfig2)).toThrow(); + + const validConfig = { + eat: { + ...VALID_ACTION_CONFIG_BASE, + networkHelpers: { + getToken: () => 'customToken', + }, + }, + }; + + expect(() => checkActionsConfig(RESOURCE_NAME, validConfig)).not.toThrow(); + }); }); diff --git a/jest.config.js b/jest.config.js index 9317c55..e2abf6d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,10 +5,10 @@ const configBase = { coverageDirectory: path.join(__dirname, 'coverage'), coverageThreshold: { global: { - branches: 80, - functions: 80, - lines: 79, statements: 80, + branches: 80, + functions: 85, + lines: 80, }, }, moduleDirectories: ['node_modules'], -- GitLab From 6b0b71cde96a47e1c6099e877f505422f883a02d Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 08:53:39 +0200 Subject: [PATCH 02/10] reset empty state resolversHashes --- .../internals/utils/resolversHashes.test.js | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/__tests__/internals/utils/resolversHashes.test.js b/__tests__/internals/utils/resolversHashes.test.js index 5799ad4..c28e59b 100644 --- a/__tests__/internals/utils/resolversHashes.test.js +++ b/__tests__/internals/utils/resolversHashes.test.js @@ -58,6 +58,10 @@ const FILLED_STATE_COMPUTED_HASHES = { PRINCIPAL_RESOURCE_IDS, ), }; +const EMPTY_STATE_RESET_HASHES = { + ...EMPTY_STATE, + resolversHashes: resetResourceResolversHashes(EMPTY_STATE, RESOURCE_NAME), +}; const FILLED_STATE_RESET_HASHES = { ...FILLED_STATE, resolversHashes: resetResourceResolversHashes(FILLED_STATE, RESOURCE_NAME), @@ -86,7 +90,27 @@ describe('computeNewResolversHashes', () => { }); describe('resetResourceResolversHashes', () => { - test('only path', () => { + test('empty state', () => { + const hashBeforeComputing = getResourceHash( + EMPTY_STATE.resolversHashes, + RESOURCE_NAME, + ); + + const hashAfterComputing = getResourceHash( + EMPTY_STATE_COMPUTED_HASHES.resolversHashes, + RESOURCE_NAME, + ); + + const hashAfterResetting = getResourceHash( + EMPTY_STATE_RESET_HASHES.resolversHashes, + RESOURCE_NAME, + ); + + expect(hashBeforeComputing).toBe(hashAfterComputing); + expect(hashBeforeComputing).toBe(hashAfterResetting); + }); + + test('filled state', () => { const hashBeforeComputing = getResourceHash( FILLED_STATE.resolversHashes, RESOURCE_NAME, -- GitLab From 1c21eae61797a8961f994f0eaeb3a2c669c16d07 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 10:16:44 +0200 Subject: [PATCH 03/10] simplify resolversHashes functions --- .../internals/utils/resolversHashes.test.js | 66 ++++++------------- .../selectors/generateActionSelectors.js | 11 ++-- .../selectors/generateResourceSelectors.js | 7 +- src/internals/utils/resolversHashes.js | 17 ++--- 4 files changed, 33 insertions(+), 68 deletions(-) diff --git a/__tests__/internals/utils/resolversHashes.test.js b/__tests__/internals/utils/resolversHashes.test.js index c28e59b..0291c41 100644 --- a/__tests__/internals/utils/resolversHashes.test.js +++ b/__tests__/internals/utils/resolversHashes.test.js @@ -71,9 +71,7 @@ describe('computeNewResolversHashes', () => { test('empty state', () => { const hashBeforeComputing = getResourcesHash(EMPTY_STATE); - const hashAfterComputing = getResourcesHash( - EMPTY_STATE_COMPUTED_HASHES.resolversHashes, - ); + const hashAfterComputing = getResourcesHash(EMPTY_STATE_COMPUTED_HASHES); expect(hashBeforeComputing).not.toBe(hashAfterComputing); }); @@ -81,9 +79,7 @@ describe('computeNewResolversHashes', () => { test('filled state', () => { const hashBeforeComputing = getResourcesHash(FILLED_STATE); - const hashAfterComputing = getResourcesHash( - FILLED_STATE_COMPUTED_HASHES.resolversHashes, - ); + const hashAfterComputing = getResourcesHash(FILLED_STATE_COMPUTED_HASHES); expect(hashBeforeComputing).not.toBe(hashAfterComputing); }); @@ -91,18 +87,15 @@ describe('computeNewResolversHashes', () => { describe('resetResourceResolversHashes', () => { test('empty state', () => { - const hashBeforeComputing = getResourceHash( - EMPTY_STATE.resolversHashes, - RESOURCE_NAME, - ); + const hashBeforeComputing = getResourceHash(EMPTY_STATE, RESOURCE_NAME); const hashAfterComputing = getResourceHash( - EMPTY_STATE_COMPUTED_HASHES.resolversHashes, + EMPTY_STATE_COMPUTED_HASHES, RESOURCE_NAME, ); const hashAfterResetting = getResourceHash( - EMPTY_STATE_RESET_HASHES.resolversHashes, + EMPTY_STATE_RESET_HASHES, RESOURCE_NAME, ); @@ -111,18 +104,15 @@ describe('resetResourceResolversHashes', () => { }); test('filled state', () => { - const hashBeforeComputing = getResourceHash( - FILLED_STATE.resolversHashes, - RESOURCE_NAME, - ); + const hashBeforeComputing = getResourceHash(FILLED_STATE, RESOURCE_NAME); const hashAfterComputing = getResourceHash( - FILLED_STATE_COMPUTED_HASHES.resolversHashes, + FILLED_STATE_COMPUTED_HASHES, RESOURCE_NAME, ); const hashAfterResetting = getResourceHash( - FILLED_STATE_RESET_HASHES.resolversHashes, + FILLED_STATE_RESET_HASHES, RESOURCE_NAME, ); @@ -147,34 +137,27 @@ describe('getPayloadIdsHash', () => { }); test('no normalizedURL', () => { - expect(getPayloadIdsHash(EMPTY_STATE_COMPUTED_HASHES.resolversHashes)).toBe( + expect(getPayloadIdsHash(EMPTY_STATE_COMPUTED_HASHES)).toBe( getEmptyResourceHash(), ); }); test('no resourceName', () => { - expect( - getPayloadIdsHash( - EMPTY_STATE_COMPUTED_HASHES.resolversHashes, - NORMALIZED_URL, - ), - ).toBe(getEmptyResourceHash()); + expect(getPayloadIdsHash(EMPTY_STATE_COMPUTED_HASHES, NORMALIZED_URL)).toBe( + getEmptyResourceHash(), + ); }); test('without computing first', () => { - expect( - getPayloadIdsHash( - FILLED_STATE.resolversHashes, - NORMALIZED_URL, - RESOURCE_NAME, - ), - ).toBe(getEmptyResourceHash()); + expect(getPayloadIdsHash(FILLED_STATE, NORMALIZED_URL, RESOURCE_NAME)).toBe( + getEmptyResourceHash(), + ); }); test('after computing', () => { expect( getPayloadIdsHash( - FILLED_STATE_COMPUTED_HASHES.resolversHashes, + FILLED_STATE_COMPUTED_HASHES, NORMALIZED_URL, RESOURCE_NAME, ), @@ -192,15 +175,11 @@ describe('getResourcesHash', () => { }); test('without computing first', () => { - expect(getResourcesHash(FILLED_STATE.resolversHashes)).toBe( - getEmptyResourceHash(), - ); + expect(getResourcesHash(FILLED_STATE)).toBe(getEmptyResourceHash()); }); test('after computing', () => { - expect( - getResourcesHash(FILLED_STATE_COMPUTED_HASHES.resolversHashes), - ).toMatchSnapshot(); + expect(getResourcesHash(FILLED_STATE_COMPUTED_HASHES)).toMatchSnapshot(); }); }); @@ -214,23 +193,20 @@ describe('getResourceHash', () => { }); test('no resourceName', () => { - expect(getResourceHash(FILLED_STATE_COMPUTED_HASHES.resolversHashes)).toBe( + expect(getResourceHash(FILLED_STATE_COMPUTED_HASHES)).toBe( getEmptyResourceHash(), ); }); test('without computing first', () => { - expect(getResourceHash(FILLED_STATE.resolversHashes, RESOURCE_NAME)).toBe( + expect(getResourceHash(FILLED_STATE, RESOURCE_NAME)).toBe( getEmptyResourceHash(), ); }); test('after computing', () => { expect( - getResourceHash( - FILLED_STATE_COMPUTED_HASHES.resolversHashes, - RESOURCE_NAME, - ), + getResourceHash(FILLED_STATE_COMPUTED_HASHES, RESOURCE_NAME), ).toMatchSnapshot(); }); }); diff --git a/src/internals/selectors/generateActionSelectors.js b/src/internals/selectors/generateActionSelectors.js index 262a63f..4e6e524 100644 --- a/src/internals/selectors/generateActionSelectors.js +++ b/src/internals/selectors/generateActionSelectors.js @@ -92,8 +92,6 @@ const payloadIdsSelector = (state, resourceName, normalizedURL) => ? state.requests[normalizedURL].payloadIds[resourceName] : null; -const resolversHashesSelector = state => state.resolversHashes; - const applyDenormalizerSelector = ( state, resourceName, @@ -134,25 +132,24 @@ const getRequestResourceResolver = ( ) => { const resource = resourceSelector(state, resourceName); const payloadIds = payloadIdsSelector(state, resourceName, normalizedURL); - const resolversHashes = resolversHashesSelector(state); if (resource && payloadIds) { return !applyDenormalizer || !denormalizer ? `${applyDenormalizer}-${getPayloadIdsHash( - resolversHashes, + state, normalizedURL, resourceName, - )}-${getResourceHash(resolversHashes, resourceName)}` + )}-${getResourceHash(state, resourceName)}` : `${applyDenormalizer}-${Object.keys( state.requests[normalizedURL].payloadIds, ) .map( resourceKey => `${getPayloadIdsHash( - resolversHashes, + state, normalizedURL, resourceKey, - )}-${getResourceHash(resolversHashes, resourceKey)}`, + )}-${getResourceHash(state, resourceKey)}`, ) .join('--')}`; } diff --git a/src/internals/selectors/generateResourceSelectors.js b/src/internals/selectors/generateResourceSelectors.js index e25ce54..f186c38 100644 --- a/src/internals/selectors/generateResourceSelectors.js +++ b/src/internals/selectors/generateResourceSelectors.js @@ -18,8 +18,6 @@ const resourceSelector = (state, resourceName) => ? state.resources[resourceName] : null; -const resolversHashesSelector = state => state.resolversHashes; - const applyDenormalizerSelector = (state, resourceName, applyDenormalizer) => applyDenormalizer; @@ -52,12 +50,11 @@ const getResourceResolver = ( denormalizer, ) => { const resource = resourceSelector(state, resourceName); - const resolversHashes = resolversHashesSelector(state); if (resource) { return !applyDenormalizer || !denormalizer - ? `${applyDenormalizer}-${getResourceHash(resolversHashes, resourceName)}` - : `${applyDenormalizer}-${getResourcesHash(resolversHashes)}`; + ? `${applyDenormalizer}-${getResourceHash(state, resourceName)}` + : `${applyDenormalizer}-${getResourcesHash(state)}`; } return getEmptyResourceHash(); diff --git a/src/internals/utils/resolversHashes.js b/src/internals/utils/resolversHashes.js index f4a4feb..bcb9c63 100644 --- a/src/internals/utils/resolversHashes.js +++ b/src/internals/utils/resolversHashes.js @@ -70,29 +70,24 @@ export const resetResourceResolversHashes = ( export const getEmptyResourceHash = () => EMPTY_HASH; export const getPayloadIdsHash = ( - resolversHashes, + { resolversHashes = {} } = {}, normalizedURL, resourceName, ) => - resolversHashes - && resolversHashes.requests + resolversHashes.requests && resolversHashes.requests[normalizedURL] && resolversHashes.requests[normalizedURL][resourceName] ? resolversHashes.requests[normalizedURL][resourceName] : EMPTY_HASH; /* eslint-disable no-underscore-dangle */ -export const getResourcesHash = resolversHashes => - resolversHashes - && resolversHashes.resources - && resolversHashes.resources._getResourcesHash +export const getResourcesHash = ({ resolversHashes = {} } = {}) => + resolversHashes.resources && resolversHashes.resources._getResourcesHash ? resolversHashes.resources._getResourcesHash() : EMPTY_HASH; /* eslint-enable no-underscore-dangle */ -export const getResourceHash = (resolversHashes, resourceName) => - resolversHashes - && resolversHashes.resources - && resolversHashes.resources[resourceName] +export const getResourceHash = ({ resolversHashes = {} } = {}, resourceName) => + resolversHashes.resources && resolversHashes.resources[resourceName] ? resolversHashes.resources[resourceName] : EMPTY_HASH; -- GitLab From efea741b83176529793416cbc9ed610ebafcd540 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 10:22:45 +0200 Subject: [PATCH 04/10] add cacheHint example --- __tests__/internals/utils/resolversHashes.test.js | 10 ++++++++-- docs/api/createResource/actionsConfig.md | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/__tests__/internals/utils/resolversHashes.test.js b/__tests__/internals/utils/resolversHashes.test.js index 0291c41..f4c8aeb 100644 --- a/__tests__/internals/utils/resolversHashes.test.js +++ b/__tests__/internals/utils/resolversHashes.test.js @@ -60,11 +60,17 @@ const FILLED_STATE_COMPUTED_HASHES = { }; const EMPTY_STATE_RESET_HASHES = { ...EMPTY_STATE, - resolversHashes: resetResourceResolversHashes(EMPTY_STATE, RESOURCE_NAME), + resolversHashes: resetResourceResolversHashes( + EMPTY_STATE_COMPUTED_HASHES, + RESOURCE_NAME, + ), }; const FILLED_STATE_RESET_HASHES = { ...FILLED_STATE, - resolversHashes: resetResourceResolversHashes(FILLED_STATE, RESOURCE_NAME), + resolversHashes: resetResourceResolversHashes( + FILLED_STATE_COMPUTED_HASHES, + RESOURCE_NAME, + ), }; describe('computeNewResolversHashes', () => { diff --git a/docs/api/createResource/actionsConfig.md b/docs/api/createResource/actionsConfig.md index 0dc1b49..fb99f15 100644 --- a/docs/api/createResource/actionsConfig.md +++ b/docs/api/createResource/actionsConfig.md @@ -24,7 +24,8 @@ const actionsConfig = { method: 'GET', url: 'https://api.co/users/:userType/::userId/infos', // Optional - beforeHook: (body, query, otherArgs, dispatch) => + cacheHint: (urlParams, query, body, otherArgs) => otherArgs.language, + beforeHook: (urlParams, query, body, otherArgs, dispatch) => console.log( 'User infos retrieved with query: ', query, -- GitLab From 4142a5904448572371787f998075e4b4752eed27 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 11:13:18 +0200 Subject: [PATCH 05/10] wip test denormalizer --- .../generateResourceSelectors.test.js | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/__tests__/internals/selectors/generateResourceSelectors.test.js b/__tests__/internals/selectors/generateResourceSelectors.test.js index bfedf5f..9560907 100644 --- a/__tests__/internals/selectors/generateResourceSelectors.test.js +++ b/__tests__/internals/selectors/generateResourceSelectors.test.js @@ -5,6 +5,29 @@ import generateResourceSelectors from '../../../src/internals/selectors/generate const { resource: { getResource, getResourceById }, } = generateResourceSelectors('fruits'); +const { + resource: { + getResource: getResourceWithDenormalizer, + getResourceById: getResourceByIdWithDenormalizer, + }, +} = generateResourceSelectors( + 'fruits', + (resourceIds, { fruits, colors } = {}) => + console.log('fruits', Object.entries(colors)) || fruits + ? { + fruits: Object.entries(fruits).reduce( + (prev, [id, fruit]) => ({ + ...prev, + [id]: { + ...fruit, + color: colors[fruit.color], + }, + }), + {}, + ), + } + : {}, +); const STARTED_AT = moment(); const ENDED_AT = moment().add(1, 'seconds'); @@ -155,6 +178,71 @@ const FAILED_RESOURCE_ID_STATE = { }, }; +const RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE = { + restEasy: { + requests: { + 'eat:https://api.co/fruits': { + resourceName: 'fruits', + resourceId: null, + startedAt: STARTED_AT, + endedAt: ENDED_AT, + hasSucceeded: true, + hasFailed: false, + payloadIds: { + fruits: [1, 2], + colors: [1, 2], + }, + }, + }, + resources: { + fruits: { + 1: { + name: 'banana', + color: '1', + }, + 2: { + name: 'cherry', + color: '2', + }, + }, + colors: { + 1: 'yellow', + 2: 'red', + }, + }, + }, +}; + +const RECEIVED_FULL_RESOURCE_ID_TO_DENORMALIZE_STATE = { + restEasy: { + requests: { + 'eat:https://api.co/fruits/2': { + resourceName: 'fruits', + resourceId: 2, + startedAt: STARTED_AT, + endedAt: ENDED_AT, + hasSucceeded: true, + hasFailed: false, + payloadIds: { + fruits: [2], + colors: [2], + }, + }, + }, + resources: { + fruits: { + 2: { + name: 'cherry', + color: '2', + }, + }, + colors: { + 2: 'red', + }, + }, + }, +}; + describe('generateResourceSelectors', () => { describe('getResource', () => { const emptyCase = state => () => { @@ -193,6 +281,46 @@ describe('generateResourceSelectors', () => { test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); }); + describe('getResource with denormalizer', () => { + const emptyCase = state => () => { + const result = getResourceWithDenormalizer(state); + + expect(result.length).toBe(0); + + const sameResult = getResourceWithDenormalizer(state); + + expect(result).toBe(sameResult); + }; + + const fullCase = state => () => { + const result = getResourceWithDenormalizer(state); + + console.log(state); + console.log(result); + + expect(result.length).toBe(3); + expect(result[0]).toBe(state.restEasy.resources.fruits['1']); + expect(result[1]).toBe(state.restEasy.resources.fruits['2']); + expect(result[2]).toBe(state.restEasy.resources.fruits['3']); + + const sameResult = getResourceWithDenormalizer(state); + + expect(result).toBe(sameResult); + }; + + test('empty state', emptyCase(EMPTY_STATE)); + test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE)); + test( + 'received empty resource state', + emptyCase(RECEIVED_EMPTY_RESOURCE_STATE), + ); + test( + 'received full resource state', + fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE), + ); + test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); + }); + describe('getResourceById', () => { const emptyCase = (state, id) => () => { expect(getResourceById(state, id)).toBeNull(); @@ -230,4 +358,42 @@ describe('generateResourceSelectors', () => { ); test('failed resource id state', emptyCase(FAILED_RESOURCE_ID_STATE, 2)); }); + + describe('getResourceById with denormalizer', () => { + const emptyCase = (state, id) => () => { + expect(getResourceByIdWithDenormalizer(state, id)).toBeNull(); + }; + + const fullCase = (state, id) => () => { + const result = getResourceByIdWithDenormalizer(state, id); + + expect(result).toBe(state.restEasy.resources.fruits[id]); + }; + + test('empty state', emptyCase(EMPTY_STATE, 2)); + test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE, 2)); + test( + 'received empty resource state', + emptyCase(RECEIVED_EMPTY_RESOURCE_STATE, 2), + ); + test( + 'received full resource state', + fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE, 2), + ); + test('failed resource state', emptyCase(FAILED_RESOURCE_STATE, 2)); + + test( + 'requested resource id state', + emptyCase(REQUESTED_RESOURCE_ID_STATE, 2), + ); + test( + 'received empty resource id state', + emptyCase(RECEIVED_EMPTY_RESOURCE_ID_STATE, 2), + ); + test( + 'received full resource id state', + fullCase(RECEIVED_FULL_RESOURCE_ID_TO_DENORMALIZE_STATE, 2), + ); + test('failed resource id state', emptyCase(FAILED_RESOURCE_ID_STATE, 2)); + }); }); -- GitLab From f3b2292f6bc9b741751e1dcb573c3e0741e01150 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 12:00:40 +0200 Subject: [PATCH 06/10] wip --- .../generateResourceSelectors.test.js | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/__tests__/internals/selectors/generateResourceSelectors.test.js b/__tests__/internals/selectors/generateResourceSelectors.test.js index 9560907..ea9cb1b 100644 --- a/__tests__/internals/selectors/generateResourceSelectors.test.js +++ b/__tests__/internals/selectors/generateResourceSelectors.test.js @@ -5,29 +5,28 @@ import generateResourceSelectors from '../../../src/internals/selectors/generate const { resource: { getResource, getResourceById }, } = generateResourceSelectors('fruits'); + +const denormalizer = (resourceIds, { fruits, colors } = {}) => + fruits + ? { + fruits: Object.entries(fruits).reduce( + (prev, [id, fruit]) => ({ + ...prev, + [id]: { + ...fruit, + color: colors[fruit.color], + }, + }), + {}, + ), + } + : {}; const { resource: { getResource: getResourceWithDenormalizer, getResourceById: getResourceByIdWithDenormalizer, }, -} = generateResourceSelectors( - 'fruits', - (resourceIds, { fruits, colors } = {}) => - console.log('fruits', Object.entries(colors)) || fruits - ? { - fruits: Object.entries(fruits).reduce( - (prev, [id, fruit]) => ({ - ...prev, - [id]: { - ...fruit, - color: colors[fruit.color], - }, - }), - {}, - ), - } - : {}, -); +} = generateResourceSelectors('fruits', denormalizer); const STARTED_AT = moment(); const ENDED_AT = moment().add(1, 'seconds'); @@ -295,8 +294,9 @@ describe('generateResourceSelectors', () => { const fullCase = state => () => { const result = getResourceWithDenormalizer(state); - console.log(state); - console.log(result); + console.log('state', state); + console.log('result', result); + console.log('denormalizer', denormalizer(null, state.restEasy.resources)); expect(result.length).toBe(3); expect(result[0]).toBe(state.restEasy.resources.fruits['1']); -- GitLab From 88e48d4771cac119d3a1060bf8607064f07bf792 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 14:13:19 +0200 Subject: [PATCH 07/10] wip tests --- .../generateResourceSelectors.test.js | 117 ++++++++++++++---- .../selectors/generateResourceSelectors.js | 26 +++- 2 files changed, 116 insertions(+), 27 deletions(-) diff --git a/__tests__/internals/selectors/generateResourceSelectors.test.js b/__tests__/internals/selectors/generateResourceSelectors.test.js index ea9cb1b..255ae64 100644 --- a/__tests__/internals/selectors/generateResourceSelectors.test.js +++ b/__tests__/internals/selectors/generateResourceSelectors.test.js @@ -8,19 +8,11 @@ const { const denormalizer = (resourceIds, { fruits, colors } = {}) => fruits - ? { - fruits: Object.entries(fruits).reduce( - (prev, [id, fruit]) => ({ - ...prev, - [id]: { - ...fruit, - color: colors[fruit.color], - }, - }), - {}, - ), - } - : {}; + ? Object.values(fruits).map(fruit => ({ + ...fruit, + color: colors[fruit.color], + })) + : []; const { resource: { getResource: getResourceWithDenormalizer, @@ -28,6 +20,14 @@ const { }, } = generateResourceSelectors('fruits', denormalizer); +const badDenormalizer = () => []; +const { + resource: { + getResource: getResourceWithBadDenormalizer, + getResourceById: getResourceByIdWithBadDenormalizer, + }, +} = generateResourceSelectors('fruits', badDenormalizer); + const STARTED_AT = moment(); const ENDED_AT = moment().add(1, 'seconds'); @@ -294,14 +294,9 @@ describe('generateResourceSelectors', () => { const fullCase = state => () => { const result = getResourceWithDenormalizer(state); - console.log('state', state); - console.log('result', result); - console.log('denormalizer', denormalizer(null, state.restEasy.resources)); - - expect(result.length).toBe(3); - expect(result[0]).toBe(state.restEasy.resources.fruits['1']); - expect(result[1]).toBe(state.restEasy.resources.fruits['2']); - expect(result[2]).toBe(state.restEasy.resources.fruits['3']); + expect(result.length).toBe(2); + expect(result[0].color).toBe(state.restEasy.resources.colors['1']); + expect(result[1].color).toBe(state.restEasy.resources.colors['2']); const sameResult = getResourceWithDenormalizer(state); @@ -321,6 +316,42 @@ describe('generateResourceSelectors', () => { test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); }); + describe('getResource with bad denormalizer', () => { + const emptyCase = state => () => { + const result = getResourceWithBadDenormalizer(state); + + expect(result.length).toBe(0); + + const sameResult = getResourceWithBadDenormalizer(state); + + expect(result).toBe(sameResult); + }; + + const fullCase = state => () => { + const result = getResourceWithBadDenormalizer(state); + + console.log(result); + + expect(result.length).toBe(0); + + const sameResult = getResourceWithBadDenormalizer(state); + + expect(result).toBe(sameResult); + }; + + test('empty state', emptyCase(EMPTY_STATE)); + test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE)); + test( + 'received empty resource state', + emptyCase(RECEIVED_EMPTY_RESOURCE_STATE), + ); + test( + 'received full resource state', + fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE), + ); + test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); + }); + describe('getResourceById', () => { const emptyCase = (state, id) => () => { expect(getResourceById(state, id)).toBeNull(); @@ -367,7 +398,49 @@ describe('generateResourceSelectors', () => { const fullCase = (state, id) => () => { const result = getResourceByIdWithDenormalizer(state, id); - expect(result).toBe(state.restEasy.resources.fruits[id]); + expect(result.color).toBe( + state.restEasy.resources.colors[ + state.restEasy.resources.fruits[id].color + ], + ); + }; + + test('empty state', emptyCase(EMPTY_STATE, 2)); + test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE, 2)); + test( + 'received empty resource state', + emptyCase(RECEIVED_EMPTY_RESOURCE_STATE, 2), + ); + test( + 'received full resource state', + fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE, 2), + ); + test('failed resource state', emptyCase(FAILED_RESOURCE_STATE, 2)); + + test( + 'requested resource id state', + emptyCase(REQUESTED_RESOURCE_ID_STATE, 2), + ); + test( + 'received empty resource id state', + emptyCase(RECEIVED_EMPTY_RESOURCE_ID_STATE, 2), + ); + test( + 'received full resource id state', + fullCase(RECEIVED_FULL_RESOURCE_ID_TO_DENORMALIZE_STATE, 2), + ); + test('failed resource id state', emptyCase(FAILED_RESOURCE_ID_STATE, 2)); + }); + + describe('getResourceById with bad denormalizer', () => { + const emptyCase = (state, id) => () => { + expect(getResourceByIdWithBadDenormalizer(state, id)).toBeNull(); + }; + + const fullCase = (state, id) => () => { + const result = getResourceByIdWithBadDenormalizer(state, id); + + expect(result).toBeNull(); }; test('empty state', emptyCase(EMPTY_STATE, 2)); diff --git a/src/internals/selectors/generateResourceSelectors.js b/src/internals/selectors/generateResourceSelectors.js index f186c38..3df82f6 100644 --- a/src/internals/selectors/generateResourceSelectors.js +++ b/src/internals/selectors/generateResourceSelectors.js @@ -75,15 +75,31 @@ const getResourceById = ( applyDenormalizer, denormalizer, ) => { - if (!applyDenormalizer || !denormalizer) { - return state.resources - && state.resources[resourceName] - && state.resources[resourceName][resourceId] + const resource + = state.resources + && state.resources[resourceName] + && state.resources[resourceName][resourceId] ? state.resources[resourceName][resourceId] : EMPTY_RESOURCE_ID; + + if (!applyDenormalizer || !denormalizer || !resource) { + return resource; } - return denormalizer([resourceId], state.resources)[0] || EMPTY_RESOURCE_ID; + const resources = Object.entries(state.resources).reduce( + (prev, [name, value]) => ({ + ...prev, + [name]: + name === resourceName + ? { + [resourceId]: resource, + } + : value, + }), + {}, + ); + + return denormalizer([resourceId], resources)[0] || EMPTY_RESOURCE_ID; }; const generateResourceSelectors = (resourceName, denormalizer) => ({ -- GitLab From d0fb60f2addab15520acb2109d765e1b2ccd8b40 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 14:15:05 +0200 Subject: [PATCH 08/10] fix hash for denormalizer --- src/internals/selectors/generateActionSelectors.js | 4 ++-- src/internals/selectors/generateResourceSelectors.js | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/internals/selectors/generateActionSelectors.js b/src/internals/selectors/generateActionSelectors.js index 4e6e524..f70dfbd 100644 --- a/src/internals/selectors/generateActionSelectors.js +++ b/src/internals/selectors/generateActionSelectors.js @@ -135,12 +135,12 @@ const getRequestResourceResolver = ( if (resource && payloadIds) { return !applyDenormalizer || !denormalizer - ? `${applyDenormalizer}-${getPayloadIdsHash( + ? `${applyDenormalizer && !!denormalizer}-${getPayloadIdsHash( state, normalizedURL, resourceName, )}-${getResourceHash(state, resourceName)}` - : `${applyDenormalizer}-${Object.keys( + : `${applyDenormalizer && !!denormalizer}-${Object.keys( state.requests[normalizedURL].payloadIds, ) .map( diff --git a/src/internals/selectors/generateResourceSelectors.js b/src/internals/selectors/generateResourceSelectors.js index 3df82f6..092f3d2 100644 --- a/src/internals/selectors/generateResourceSelectors.js +++ b/src/internals/selectors/generateResourceSelectors.js @@ -53,8 +53,11 @@ const getResourceResolver = ( if (resource) { return !applyDenormalizer || !denormalizer - ? `${applyDenormalizer}-${getResourceHash(state, resourceName)}` - : `${applyDenormalizer}-${getResourcesHash(state)}`; + ? `${applyDenormalizer && !!denormalizer}-${getResourceHash( + state, + resourceName, + )}` + : `${applyDenormalizer && !!denormalizer}-${getResourcesHash(state)}`; } return getEmptyResourceHash(); -- GitLab From 6a88b31e81fe5945ccdcf6571c3e9e5855ec7cd4 Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 14:16:14 +0200 Subject: [PATCH 09/10] clarify --- src/internals/selectors/generateActionSelectors.js | 6 +++--- src/internals/selectors/generateResourceSelectors.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/internals/selectors/generateActionSelectors.js b/src/internals/selectors/generateActionSelectors.js index f70dfbd..85b94f6 100644 --- a/src/internals/selectors/generateActionSelectors.js +++ b/src/internals/selectors/generateActionSelectors.js @@ -134,13 +134,13 @@ const getRequestResourceResolver = ( const payloadIds = payloadIdsSelector(state, resourceName, normalizedURL); if (resource && payloadIds) { - return !applyDenormalizer || !denormalizer - ? `${applyDenormalizer && !!denormalizer}-${getPayloadIdsHash( + return !(applyDenormalizer && denormalizer) + ? `${!!(applyDenormalizer && denormalizer)}-${getPayloadIdsHash( state, normalizedURL, resourceName, )}-${getResourceHash(state, resourceName)}` - : `${applyDenormalizer && !!denormalizer}-${Object.keys( + : `${!!(applyDenormalizer && denormalizer)}-${Object.keys( state.requests[normalizedURL].payloadIds, ) .map( diff --git a/src/internals/selectors/generateResourceSelectors.js b/src/internals/selectors/generateResourceSelectors.js index 092f3d2..806e950 100644 --- a/src/internals/selectors/generateResourceSelectors.js +++ b/src/internals/selectors/generateResourceSelectors.js @@ -52,12 +52,12 @@ const getResourceResolver = ( const resource = resourceSelector(state, resourceName); if (resource) { - return !applyDenormalizer || !denormalizer - ? `${applyDenormalizer && !!denormalizer}-${getResourceHash( + return !(applyDenormalizer && denormalizer) + ? `${!!(applyDenormalizer && denormalizer)}-${getResourceHash( state, resourceName, )}` - : `${applyDenormalizer && !!denormalizer}-${getResourcesHash(state)}`; + : `${!!(applyDenormalizer && denormalizer)}-${getResourcesHash(state)}`; } return getEmptyResourceHash(); -- GitLab From a988a5ecd8851f55533603f12258277a22258bde Mon Sep 17 00:00:00 2001 From: Adrien HARNAY <adrien@harnay.me> Date: Thu, 26 Apr 2018 14:35:09 +0200 Subject: [PATCH 10/10] revert trials --- .../generateResourceSelectors.test.js | 144 ++++-------------- 1 file changed, 31 insertions(+), 113 deletions(-) diff --git a/__tests__/internals/selectors/generateResourceSelectors.test.js b/__tests__/internals/selectors/generateResourceSelectors.test.js index 255ae64..ffd8287 100644 --- a/__tests__/internals/selectors/generateResourceSelectors.test.js +++ b/__tests__/internals/selectors/generateResourceSelectors.test.js @@ -20,14 +20,6 @@ const { }, } = generateResourceSelectors('fruits', denormalizer); -const badDenormalizer = () => []; -const { - resource: { - getResource: getResourceWithBadDenormalizer, - getResourceById: getResourceByIdWithBadDenormalizer, - }, -} = generateResourceSelectors('fruits', badDenormalizer); - const STARTED_AT = moment(); const ENDED_AT = moment().add(1, 'seconds'); @@ -280,78 +272,6 @@ describe('generateResourceSelectors', () => { test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); }); - describe('getResource with denormalizer', () => { - const emptyCase = state => () => { - const result = getResourceWithDenormalizer(state); - - expect(result.length).toBe(0); - - const sameResult = getResourceWithDenormalizer(state); - - expect(result).toBe(sameResult); - }; - - const fullCase = state => () => { - const result = getResourceWithDenormalizer(state); - - expect(result.length).toBe(2); - expect(result[0].color).toBe(state.restEasy.resources.colors['1']); - expect(result[1].color).toBe(state.restEasy.resources.colors['2']); - - const sameResult = getResourceWithDenormalizer(state); - - expect(result).toBe(sameResult); - }; - - test('empty state', emptyCase(EMPTY_STATE)); - test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE)); - test( - 'received empty resource state', - emptyCase(RECEIVED_EMPTY_RESOURCE_STATE), - ); - test( - 'received full resource state', - fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE), - ); - test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); - }); - - describe('getResource with bad denormalizer', () => { - const emptyCase = state => () => { - const result = getResourceWithBadDenormalizer(state); - - expect(result.length).toBe(0); - - const sameResult = getResourceWithBadDenormalizer(state); - - expect(result).toBe(sameResult); - }; - - const fullCase = state => () => { - const result = getResourceWithBadDenormalizer(state); - - console.log(result); - - expect(result.length).toBe(0); - - const sameResult = getResourceWithBadDenormalizer(state); - - expect(result).toBe(sameResult); - }; - - test('empty state', emptyCase(EMPTY_STATE)); - test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE)); - test( - 'received empty resource state', - emptyCase(RECEIVED_EMPTY_RESOURCE_STATE), - ); - test( - 'received full resource state', - fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE), - ); - test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); - }); - describe('getResourceById', () => { const emptyCase = (state, id) => () => { expect(getResourceById(state, id)).toBeNull(); @@ -390,57 +310,55 @@ describe('generateResourceSelectors', () => { test('failed resource id state', emptyCase(FAILED_RESOURCE_ID_STATE, 2)); }); - describe('getResourceById with denormalizer', () => { - const emptyCase = (state, id) => () => { - expect(getResourceByIdWithDenormalizer(state, id)).toBeNull(); + describe('getResource with denormalizer', () => { + const emptyCase = state => () => { + const result = getResourceWithDenormalizer(state); + + expect(result.length).toBe(0); + + const sameResult = getResourceWithDenormalizer(state); + + expect(result).toBe(sameResult); }; - const fullCase = (state, id) => () => { - const result = getResourceByIdWithDenormalizer(state, id); + const fullCase = state => () => { + const result = getResourceWithDenormalizer(state); - expect(result.color).toBe( - state.restEasy.resources.colors[ - state.restEasy.resources.fruits[id].color - ], - ); + expect(result.length).toBe(2); + expect(result[0].color).toBe(state.restEasy.resources.colors['1']); + expect(result[1].color).toBe(state.restEasy.resources.colors['2']); + + const sameResult = getResourceWithDenormalizer(state); + + expect(result).toBe(sameResult); }; - test('empty state', emptyCase(EMPTY_STATE, 2)); - test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE, 2)); + test('empty state', emptyCase(EMPTY_STATE)); + test('requested resource state', emptyCase(REQUESTED_RESOURCE_STATE)); test( 'received empty resource state', - emptyCase(RECEIVED_EMPTY_RESOURCE_STATE, 2), + emptyCase(RECEIVED_EMPTY_RESOURCE_STATE), ); test( 'received full resource state', - fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE, 2), - ); - test('failed resource state', emptyCase(FAILED_RESOURCE_STATE, 2)); - - test( - 'requested resource id state', - emptyCase(REQUESTED_RESOURCE_ID_STATE, 2), - ); - test( - 'received empty resource id state', - emptyCase(RECEIVED_EMPTY_RESOURCE_ID_STATE, 2), - ); - test( - 'received full resource id state', - fullCase(RECEIVED_FULL_RESOURCE_ID_TO_DENORMALIZE_STATE, 2), + fullCase(RECEIVED_FULL_RESOURCE_TO_DENORMALIZE_STATE), ); - test('failed resource id state', emptyCase(FAILED_RESOURCE_ID_STATE, 2)); + test('failed resource state', emptyCase(FAILED_RESOURCE_STATE)); }); - describe('getResourceById with bad denormalizer', () => { + describe('getResourceById with denormalizer', () => { const emptyCase = (state, id) => () => { - expect(getResourceByIdWithBadDenormalizer(state, id)).toBeNull(); + expect(getResourceByIdWithDenormalizer(state, id)).toBeNull(); }; const fullCase = (state, id) => () => { - const result = getResourceByIdWithBadDenormalizer(state, id); + const result = getResourceByIdWithDenormalizer(state, id); - expect(result).toBeNull(); + expect(result.color).toBe( + state.restEasy.resources.colors[ + state.restEasy.resources.fruits[id].color + ], + ); }; test('empty state', emptyCase(EMPTY_STATE, 2)); -- GitLab