From c0857eacaecc994e391e0c2e458779e20525628e Mon Sep 17 00:00:00 2001 From: epiqueras Date: Sat, 16 Nov 2019 08:08:57 -0800 Subject: [PATCH 1/4] Core Data: Add checks to selectors to avoid throwing errors when the relevant config is not loaded. --- packages/core-data/src/actions.js | 7 ++----- packages/core-data/src/selectors.js | 5 ++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index a79aaba7e33d8..adb2837225ea4 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -136,11 +136,8 @@ export function receiveEmbedPreview( url, preview ) { * @return {Object} Action object. */ export function* editEntityRecord( kind, name, recordId, edits, options = {} ) { - const { transientEdits = {}, mergedEdits = {} } = yield select( - 'getEntity', - kind, - name - ); + const { transientEdits = {}, mergedEdits = {} } = + ( yield select( 'getEntity', kind, name ) ) || {}; const record = yield select( 'getRawEntityRecord', kind, name, recordId ); const editedRecord = yield select( 'getEditedEntityRecord', diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 3974d66c83b60..8bad0c661dbeb 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -183,8 +183,11 @@ export function getEntityRecordEdits( state, kind, name, recordId ) { */ export const getEntityRecordNonTransientEdits = createSelector( ( state, kind, name, recordId ) => { - const { transientEdits = {} } = getEntity( state, kind, name ); + const { transientEdits } = getEntity( state, kind, name ) || {}; const edits = getEntityRecordEdits( state, kind, name, recordId ) || []; + if ( ! transientEdits ) { + return edits; + } return Object.keys( edits ).reduce( ( acc, key ) => { if ( ! transientEdits[ key ] ) { acc[ key ] = edits[ key ]; From 54adc4268bf25d5cdc80915274a66de5814ea4ec Mon Sep 17 00:00:00 2001 From: epiqueras Date: Tue, 19 Nov 2019 14:12:07 -0800 Subject: [PATCH 2/4] Core Data: Throw when `editEntityRecord` targets an entity without a config. --- packages/core-data/src/actions.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index adb2837225ea4..66fbd292ec340 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -136,8 +136,11 @@ export function receiveEmbedPreview( url, preview ) { * @return {Object} Action object. */ export function* editEntityRecord( kind, name, recordId, edits, options = {} ) { - const { transientEdits = {}, mergedEdits = {} } = - ( yield select( 'getEntity', kind, name ) ) || {}; + const entity = yield select( 'getEntity', kind, name ); + if ( ! entity ) { + throw new Error( `The entity being edited (${ kind }, ${ name }) does not have a loaded config.` ); + } + const { transientEdits = {}, mergedEdits = {} } = entity; const record = yield select( 'getRawEntityRecord', kind, name, recordId ); const editedRecord = yield select( 'getEditedEntityRecord', From a00cb9985adcc070224bdecf07ccd2ab9a51f654 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Tue, 19 Nov 2019 14:12:50 -0800 Subject: [PATCH 3/4] Core Data: Fix default value typo in `getEntityRecordNonTransientEdits`. --- packages/core-data/src/selectors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 8bad0c661dbeb..1475ffd2562f8 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -184,7 +184,7 @@ export function getEntityRecordEdits( state, kind, name, recordId ) { export const getEntityRecordNonTransientEdits = createSelector( ( state, kind, name, recordId ) => { const { transientEdits } = getEntity( state, kind, name ) || {}; - const edits = getEntityRecordEdits( state, kind, name, recordId ) || []; + const edits = getEntityRecordEdits( state, kind, name, recordId ) || {}; if ( ! transientEdits ) { return edits; } From 8a3dc8a11282cfec966cabebba97f027ca94e0ef Mon Sep 17 00:00:00 2001 From: epiqueras Date: Tue, 19 Nov 2019 14:13:59 -0800 Subject: [PATCH 4/4] Core Data: Test the behavior of `editEntityRecord` and `getEntityRecordNonTransientEdits` on entities without configs. --- packages/core-data/src/test/actions.js | 29 +++++++++++++++++++++++- packages/core-data/src/test/selectors.js | 12 ++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/core-data/src/test/actions.js b/packages/core-data/src/test/actions.js index cf839cf9a3493..ffcee0efbca50 100644 --- a/packages/core-data/src/test/actions.js +++ b/packages/core-data/src/test/actions.js @@ -1,7 +1,34 @@ /** * Internal dependencies */ -import { saveEntityRecord, receiveEntityRecords, receiveUserPermission, receiveAutosaves, receiveCurrentUser } from '../actions'; +import { + editEntityRecord, + saveEntityRecord, + receiveEntityRecords, + receiveUserPermission, + receiveAutosaves, + receiveCurrentUser, +} from '../actions'; +import { select } from '../controls'; + +describe( 'editEntityRecord', () => { + it( 'throws when the edited entity does not have a loaded config.', () => { + const entity = { kind: 'someKind', name: 'someName', id: 'someId' }; + const fulfillment = editEntityRecord( + entity.kind, + entity.name, + entity.id, + {} + ); + expect( fulfillment.next().value ).toEqual( + select( 'getEntity', entity.kind, entity.name ) + ); + // Don't pass back an entity config. + expect( fulfillment.next.bind( fulfillment ) ).toThrow( + `The entity being edited (${ entity.kind }, ${ entity.name }) does not have a loaded config.` + ); + } ); +} ); describe( 'saveEntityRecord', () => { it( 'triggers a POST request for a new record', async () => { diff --git a/packages/core-data/src/test/selectors.js b/packages/core-data/src/test/selectors.js index a52c13d99c7d1..13e1551f2f1d6 100644 --- a/packages/core-data/src/test/selectors.js +++ b/packages/core-data/src/test/selectors.js @@ -9,6 +9,7 @@ import deepFreeze from 'deep-freeze'; import { getEntityRecord, getEntityRecords, + getEntityRecordNonTransientEdits, getEmbedPreview, isPreviewEmbedFallback, canUser, @@ -104,6 +105,17 @@ describe( 'getEntityRecords', () => { } ); } ); +describe( 'getEntityRecordNonTransientEdits', () => { + it( 'should return an empty object when the entity does not have a loaded config.', () => { + const state = deepFreeze( { + entities: { config: {}, data: {} }, + } ); + expect( + getEntityRecordNonTransientEdits( state, 'someKind', 'someName', 'someId' ) + ).toEqual( {} ); + } ); +} ); + describe( 'getEmbedPreview()', () => { it( 'returns preview stored for url', () => { let state = deepFreeze( {