Skip to content

Commit

Permalink
Core Data: Add the 'getEntitiesConfig' resolver (#65871)
Browse files Browse the repository at this point in the history
Co-authored-by: Mamaduka <[email protected]>
Co-authored-by: ellatrix <[email protected]>
Co-authored-by: youknowriad <[email protected]>
  • Loading branch information
4 people authored Oct 30, 2024
1 parent f0ef1ac commit 5432049
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 259 deletions.
20 changes: 10 additions & 10 deletions packages/core-data/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import deprecated from '@wordpress/deprecated';
*/
import { getNestedValue, setNestedValue } from './utils';
import { receiveItems, removeItems, receiveQueriedItems } from './queried-data';
import { getOrLoadEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities';
import { DEFAULT_ENTITY_KEY } from './entities';
import { createBatch } from './batch';
import { STORE_NAME } from './name';
import { getSyncProvider } from './sync';
Expand Down Expand Up @@ -285,8 +285,8 @@ export const deleteEntityRecord =
query,
{ __unstableFetch = apiFetch, throwOnError = false } = {}
) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
async ( { dispatch, resolveSelect } ) => {
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -503,7 +503,7 @@ export const saveEntityRecord =
} = {}
) =>
async ( { select, resolveSelect, dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -780,11 +780,11 @@ export const __experimentalBatch =
*/
export const saveEditedEntityRecord =
( kind, name, recordId, options ) =>
async ( { select, dispatch } ) => {
async ( { select, dispatch, resolveSelect } ) => {
if ( ! select.hasEditsForEntityRecord( kind, name, recordId ) ) {
return;
}
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -813,7 +813,7 @@ export const saveEditedEntityRecord =
*/
export const __experimentalSaveSpecifiedEntityEdits =
( kind, name, recordId, itemsToSave, options ) =>
async ( { select, dispatch } ) => {
async ( { select, dispatch, resolveSelect } ) => {
if ( ! select.hasEditsForEntityRecord( kind, name, recordId ) ) {
return;
}
Expand All @@ -828,7 +828,7 @@ export const __experimentalSaveSpecifiedEntityEdits =
setNestedValue( editsToSave, item, getNestedValue( edits, item ) );
}

const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -973,8 +973,8 @@ export function receiveDefaultTemplateId( query, templateId ) {
*/
export const receiveRevisions =
( kind, name, recordKey, records, query, invalidateCache = false, meta ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
async ( { dispatch, resolveSelect } ) => {
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down
64 changes: 0 additions & 64 deletions packages/core-data/src/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@ import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';
import { RichTextData } from '@wordpress/rich-text';

/**
* Internal dependencies
*/
import { addEntities } from './actions';
import { getSyncProvider } from './sync';

export const DEFAULT_ENTITY_KEY = 'id';

const POST_RAW_ATTRIBUTES = [ 'title', 'excerpt', 'content' ];

export const rootEntitiesConfig = [
Expand Down Expand Up @@ -458,60 +451,3 @@ export const getMethodName = ( kind, name, prefix = 'get' ) => {
const suffix = pascalCase( name );
return `${ prefix }${ kindPrefix }${ suffix }`;
};

function registerSyncConfigs( configs ) {
configs.forEach( ( { syncObjectType, syncConfig } ) => {
getSyncProvider().register( syncObjectType, syncConfig );
const editSyncConfig = { ...syncConfig };
delete editSyncConfig.fetch;
getSyncProvider().register( syncObjectType + '--edit', editSyncConfig );
} );
}

/**
* Loads the entities into the store.
*
* Note: The `name` argument is used for `root` entities requiring additional server data.
*
* @param {string} kind Kind
* @param {string} name Name
* @return {(thunkArgs: object) => Promise<Array>} Entities
*/
export const getOrLoadEntitiesConfig =
( kind, name ) =>
async ( { select, dispatch } ) => {
let configs = select.getEntitiesConfig( kind );
const hasConfig = !! select.getEntityConfig( kind, name );

if ( configs?.length > 0 && hasConfig ) {
if ( window.__experimentalEnableSync ) {
if ( globalThis.IS_GUTENBERG_PLUGIN ) {
registerSyncConfigs( configs );
}
}

return configs;
}

const loader = additionalEntityConfigLoaders.find( ( l ) => {
if ( ! name || ! l.name ) {
return l.kind === kind;
}

return l.kind === kind && l.name === name;
} );
if ( ! loader ) {
return [];
}

configs = await loader.loadEntities();
if ( window.__experimentalEnableSync ) {
if ( globalThis.IS_GUTENBERG_PLUGIN ) {
registerSyncConfigs( configs );
}
}

dispatch( addEntities( configs ) );

return configs;
};
11 changes: 9 additions & 2 deletions packages/core-data/src/hooks/test/use-entity-record.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@ describe( 'useEntityRecord', () => {
await act(
() => new Promise( ( resolve ) => setTimeout( resolve, 0 ) )
);
expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
await waitFor( () =>
expect( triggerFetch ).toHaveBeenCalledWith( {
path: '/wp/v2/widgets/1?context=edit',
parse: false,
} )
);
// Clear the fetch call history.
triggerFetch.mockReset();

rerender( <UI enabled={ false } /> );

Expand All @@ -157,6 +164,6 @@ describe( 'useEntityRecord', () => {
await act(
() => new Promise( ( resolve ) => setTimeout( resolve, 0 ) )
);
expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
expect( triggerFetch ).toHaveBeenCalledTimes( 0 );
} );
} );
86 changes: 57 additions & 29 deletions packages/core-data/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import apiFetch from '@wordpress/api-fetch';
* Internal dependencies
*/
import { STORE_NAME } from './name';
import { getOrLoadEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities';
import { additionalEntityConfigLoaders, DEFAULT_ENTITY_KEY } from './entities';
import {
forwardResolver,
getNormalizedCommaSeparable,
Expand Down Expand Up @@ -64,8 +64,8 @@ export const getCurrentUser =
*/
export const getEntityRecord =
( kind, name, key = '', query ) =>
async ( { select, dispatch, registry } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
async ( { select, dispatch, registry, resolveSelect } ) => {
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -230,8 +230,8 @@ export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
*/
export const getEntityRecords =
( kind, name, query = {} ) =>
async ( { dispatch, registry } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
async ( { dispatch, registry, resolveSelect } ) => {
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -431,19 +431,36 @@ export const getEmbedPreview =
*/
export const canUser =
( requestedAction, resource, id ) =>
async ( { dispatch, registry } ) => {
async ( { dispatch, registry, resolveSelect } ) => {
if ( ! ALLOWED_RESOURCE_ACTIONS.includes( requestedAction ) ) {
throw new Error( `'${ requestedAction }' is not a valid action.` );
}

const { hasStartedResolution } = registry.select( STORE_NAME );

// Prevent resolving the same resource twice.
for ( const relatedAction of ALLOWED_RESOURCE_ACTIONS ) {
if ( relatedAction === requestedAction ) {
continue;
}
const isAlreadyResolving = hasStartedResolution( 'canUser', [
relatedAction,
resource,
id,
] );
if ( isAlreadyResolving ) {
return;
}
}

let resourcePath = null;
if ( typeof resource === 'object' ) {
if ( ! resource.kind || ! resource.name ) {
throw new Error( 'The entity resource object is not valid.' );
}

const configs = await dispatch(
getOrLoadEntitiesConfig( resource.kind, resource.name )
const configs = await resolveSelect.getEntitiesConfig(
resource.kind
);
const entityConfig = configs.find(
( config ) =>
Expand All @@ -460,23 +477,6 @@ export const canUser =
resourcePath = `/wp/v2/${ resource }` + ( id ? '/' + id : '' );
}

const { hasStartedResolution } = registry.select( STORE_NAME );

// Prevent resolving the same resource twice.
for ( const relatedAction of ALLOWED_RESOURCE_ACTIONS ) {
if ( relatedAction === requestedAction ) {
continue;
}
const isAlreadyResolving = hasStartedResolution( 'canUser', [
relatedAction,
resource,
id,
] );
if ( isAlreadyResolving ) {
return;
}
}

let response;
try {
response = await apiFetch( {
Expand Down Expand Up @@ -823,8 +823,8 @@ export const getDefaultTemplateId =
*/
export const getRevisions =
( kind, name, recordKey, query = {} ) =>
async ( { dispatch, registry } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
async ( { dispatch, registry, resolveSelect } ) => {
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -944,8 +944,8 @@ getRevisions.shouldInvalidate = ( action, kind, name, recordKey ) =>
*/
export const getRevision =
( kind, name, recordKey, revisionKey, query ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
async ( { dispatch, resolveSelect } ) => {
const configs = await resolveSelect.getEntitiesConfig( kind );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -1017,3 +1017,31 @@ export const getRegisteredPostMeta =
);
}
};

/**
* Requests entity configs for the given kind from the REST API.
*
* @param {string} kind Entity kind.
*/
export const getEntitiesConfig =
( kind ) =>
async ( { dispatch } ) => {
const loader = additionalEntityConfigLoaders.find(
( l ) => l.kind === kind
);

if ( ! loader ) {
return;
}

try {
const configs = await loader.loadEntities();
if ( ! configs.length ) {
return;
}

dispatch.addEntities( configs );
} catch {
// Do nothing if the request comes back with an API error.
}
};
Loading

1 comment on commit 5432049

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 5432049.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/11592700685
📝 Reported issues:

Please sign in to comment.