-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Core Data: Don't return partial data when selecting a complete item #71474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2419866
0e2eb9f
d1b7dca
b3b896b
f0c164d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -365,7 +365,7 @@ export const getEntityRecord = createSelector( | |||
| } | ||||
| const context = query?.context ?? 'default'; | ||||
|
|
||||
| if ( query === undefined ) { | ||||
| if ( ! query || ! query._fields ) { | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this now make the gutenberg/packages/core-data/src/selectors.ts Line 378 in 0e2eb9f
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's good practice to check argument existence before iterating on them. I think we should leave the linked condition.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I mean is that line 378 can now never be reached if
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. I don't have a strong opinion here, but it seems like a good check to keep in place "just in case" 😄 Updated in d1b7dca. |
||||
| // If expecting a complete item, validate that completeness. | ||||
| if ( ! queriedState.itemIsComplete[ context ]?.[ key ] ) { | ||||
| return undefined; | ||||
|
|
@@ -375,30 +375,29 @@ export const getEntityRecord = createSelector( | |||
| } | ||||
|
|
||||
| const item = queriedState.items[ context ]?.[ key ]; | ||||
| if ( item && query._fields ) { | ||||
| const filteredItem = {}; | ||||
| const fields = getNormalizedCommaSeparable( query._fields ) ?? []; | ||||
| for ( let f = 0; f < fields.length; f++ ) { | ||||
| const field = fields[ f ].split( '.' ); | ||||
| let value = item; | ||||
| field.forEach( ( fieldName ) => { | ||||
| value = value?.[ fieldName ]; | ||||
| } ); | ||||
| setNestedValue( filteredItem, field, value ); | ||||
| } | ||||
| return filteredItem as EntityRecord; | ||||
| if ( ! item ) { | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the flattening with early return 👍 |
||||
| return item; | ||||
| } | ||||
|
|
||||
| return item; | ||||
| const filteredItem = {}; | ||||
| const fields = getNormalizedCommaSeparable( query._fields ) ?? []; | ||||
| for ( let f = 0; f < fields.length; f++ ) { | ||||
| const field = fields[ f ].split( '.' ); | ||||
| let value = item; | ||||
| field.forEach( ( fieldName ) => { | ||||
| value = value?.[ fieldName ]; | ||||
| } ); | ||||
| setNestedValue( filteredItem, field, value ); | ||||
| } | ||||
| return filteredItem as EntityRecord; | ||||
| } ) as GetEntityRecord, | ||||
| ( state: State, kind, name, recordId, query ) => { | ||||
| const context = query?.context ?? 'default'; | ||||
| const queriedState = | ||||
| state.entities.records?.[ kind ]?.[ name ]?.queriedData; | ||||
|
Comment on lines
+396
to
+397
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just extracted it into a reusable variable. |
||||
| return [ | ||||
| state.entities.records?.[ kind ]?.[ name ]?.queriedData?.items[ | ||||
| context | ||||
| ]?.[ recordId ], | ||||
| state.entities.records?.[ kind ]?.[ name ]?.queriedData | ||||
| ?.itemIsComplete[ context ]?.[ recordId ], | ||||
| queriedState?.items[ context ]?.[ recordId ], | ||||
| queriedState?.itemIsComplete[ context ]?.[ recordId ], | ||||
| ]; | ||||
| } | ||||
| ) as GetEntityRecord; | ||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,6 @@ import deepFreeze from 'deep-freeze'; | |
| */ | ||
| import { | ||
| getEntityRecord, | ||
| __experimentalGetEntityRecordNoResolver, | ||
| hasEntityRecords, | ||
| getEntityRecords, | ||
| getRawEntityRecord, | ||
|
|
@@ -25,11 +24,7 @@ import { | |
| getRevision, | ||
| } from '../selectors'; | ||
|
|
||
| // getEntityRecord and __experimentalGetEntityRecordNoResolver selectors share the same tests. | ||
| describe.each( [ | ||
| [ getEntityRecord ], | ||
| [ __experimentalGetEntityRecordNoResolver ], | ||
| ] )( '%p', ( selector ) => { | ||
|
Comment on lines
-28
to
-32
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed coverage for I think we should just remove or deprecate it. |
||
| describe( 'getEntityRecord', () => { | ||
| describe( 'normalizing Post ID passed as recordKey', () => { | ||
| it( 'normalizes any Post ID recordKey argument to a Number via `__unstableNormalizeArgs` method', async () => { | ||
| const normalized = getEntityRecord.__unstableNormalizeArgs( [ | ||
|
|
@@ -70,7 +65,7 @@ describe.each( [ | |
| }, | ||
| }, | ||
| } ); | ||
| expect( selector( state, 'foo', 'bar', 'baz' ) ).toBeUndefined(); | ||
| expect( getEntityRecord( state, 'foo', 'bar', 'baz' ) ).toBeUndefined(); | ||
| } ); | ||
|
|
||
| it( 'should return undefined for unknown record’s key', () => { | ||
|
|
@@ -89,7 +84,9 @@ describe.each( [ | |
| }, | ||
| }, | ||
| } ); | ||
| expect( selector( state, 'root', 'postType', 'post' ) ).toBeUndefined(); | ||
| expect( | ||
| getEntityRecord( state, 'root', 'postType', 'post' ) | ||
| ).toBeUndefined(); | ||
| } ); | ||
|
|
||
| it( 'should return a record by key', () => { | ||
|
|
@@ -116,16 +113,98 @@ describe.each( [ | |
| }, | ||
| }, | ||
| } ); | ||
| expect( selector( state, 'root', 'postType', 'post' ) ).toEqual( { | ||
| slug: 'post', | ||
| expect( getEntityRecord( state, 'root', 'postType', 'post' ) ).toEqual( | ||
| { | ||
| slug: 'post', | ||
| } | ||
| ); | ||
| } ); | ||
|
|
||
| it( 'should return undefined if no item received, filtered item requested', () => { | ||
| const state = deepFreeze( { | ||
| entities: { | ||
| records: { | ||
| root: { | ||
| postType: { | ||
| queriedData: { | ||
| items: {}, | ||
| itemIsComplete: {}, | ||
| queries: {}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } ); | ||
|
|
||
| expect( | ||
| getEntityRecord( state, 'root', 'postType', 'post', { | ||
| _fields: 'content', | ||
| } ) | ||
| ).toBeUndefined(); | ||
| } ); | ||
|
|
||
| it( 'should return null if no item received, filtered item requested', () => {} ); | ||
| it( 'should return filtered item if incomplete item received, filtered item requested', () => { | ||
| const state = deepFreeze( { | ||
| entities: { | ||
| records: { | ||
| root: { | ||
| postType: { | ||
| queriedData: { | ||
| items: { | ||
| default: { | ||
| post: { | ||
| content: 'chicken', | ||
| author: 'bob', | ||
| }, | ||
| }, | ||
| }, | ||
| itemIsComplete: {}, | ||
| queries: {}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } ); | ||
|
|
||
| it( 'should return filtered item if incomplete item received, filtered item requested', () => {} ); | ||
| expect( | ||
| getEntityRecord( state, 'root', 'postType', 'post', { | ||
| _fields: 'content', | ||
| } ) | ||
| ).toEqual( { content: 'chicken' } ); | ||
| } ); | ||
|
|
||
| it( 'should return null if incomplete item received, complete item requested', () => {} ); | ||
| it( 'should return undefined if incomplete item received, complete item requested', () => { | ||
| const state = deepFreeze( { | ||
| entities: { | ||
| records: { | ||
| root: { | ||
| postType: { | ||
| queriedData: { | ||
| items: { | ||
| default: { | ||
| post: { | ||
| content: 'chicken', | ||
| author: 'bob', | ||
| }, | ||
| }, | ||
| }, | ||
| itemIsComplete: {}, | ||
| queries: {}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } ); | ||
|
|
||
| expect( | ||
| getEntityRecord( state, 'root', 'postType', 'post', { | ||
| context: 'default', | ||
| } ) | ||
| ).toBeUndefined(); | ||
| } ); | ||
|
|
||
| it( 'should return filtered item if complete item received, filtered item requested', () => { | ||
| const state = deepFreeze( { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new condition is the same as that used in the reducer.
gutenberg/packages/core-data/src/queried-data/reducer.js
Lines 153 to 161 in 2e69c4a