diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index ba77f065584cfe..23503ce411eda5 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -18,7 +18,7 @@ _Parameters_ - _state_ `State`: Data state. - _action_ `string`: Action to check. One of: 'create', 'read', 'update', 'delete'. -- _resource_ `string`: REST resource to check, e.g. 'media' or 'posts'. +- _resource_ `string | Record< string, any >`: REST resource to check, e.g. 'media' or 'posts'. - _id_ `EntityRecordKey`: Optional ID of the rest resource to check. _Returns_ diff --git a/packages/core-data/README.md b/packages/core-data/README.md index 694f780dafb99d..90f53a61a28331 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -339,7 +339,7 @@ _Parameters_ - _state_ `State`: Data state. - _action_ `string`: Action to check. One of: 'create', 'read', 'update', 'delete'. -- _resource_ `string`: REST resource to check, e.g. 'media' or 'posts'. +- _resource_ `string | Record< string, any >`: REST resource to check, e.g. 'media' or 'posts'. - _id_ `EntityRecordKey`: Optional ID of the rest resource to check. _Returns_ diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index bff8c8cb0f6780..a6a6a86a732af8 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -360,15 +360,35 @@ export const getEmbedPreview = export const canUser = ( requestedAction, resource, id ) => async ( { dispatch, registry } ) => { - const { hasStartedResolution } = registry.select( STORE_NAME ); - - const resourcePath = id ? `${ resource }/${ id }` : resource; const retrievedActions = [ 'create', 'read', 'update', 'delete' ]; if ( ! retrievedActions.includes( requestedAction ) ) { throw new Error( `'${ requestedAction }' is not a valid action.` ); } + let resourcePath = null; + if ( typeof resource === 'object' ) { + const configs = await dispatch( + getOrLoadEntitiesConfig( resource.kind, resource.name ) + ); + const entityConfig = configs.find( + ( config ) => + config.name === resource.name && + config.kind === resource.name + ); + if ( ! entityConfig ) { + return; + } + + resourcePath = + entityConfig.baseURL + ( resource.id ? '/' + resource.id : '' ); + } else { + // @todo: Maybe warn when detecting a legacy usage. + resourcePath = `/wp/v2/${ resource }` + ( id ? '/' + id : '' ); + } + + const { hasStartedResolution } = registry.select( STORE_NAME ); + // Prevent resolving the same resource twice. for ( const relatedAction of retrievedActions ) { if ( relatedAction === requestedAction ) { @@ -387,7 +407,7 @@ export const canUser = let response; try { response = await apiFetch( { - path: `/wp/v2/${ resourcePath }`, + path: resourcePath, method: 'OPTIONS', parse: false, } ); @@ -416,10 +436,15 @@ export const canUser = registry.batch( () => { for ( const action of retrievedActions ) { - dispatch.receiveUserPermission( - `${ action }/${ resourcePath }`, - permissions[ action ] - ); + const key = ( + typeof resource === 'object' + ? [ action, resource.kind, resource.name, resource.id ] + : [ action, resource, id ] + ) + .filter( Boolean ) + .join( '/' ); + + dispatch.receiveUserPermission( key, permissions[ action ] ); } } ); }; diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index 425a537cad7363..7d3e76ca988a1f 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -1145,10 +1145,17 @@ export function isPreviewEmbedFallback( state: State, url: string ): boolean { export function canUser( state: State, action: string, - resource: string, + resource: string | Record< string, any >, id?: EntityRecordKey ): boolean | undefined { - const key = [ action, resource, id ].filter( Boolean ).join( '/' ); + const key = ( + typeof resource === 'object' + ? [ action, resource.kind, resource.name, resource.id ] + : [ action, resource, id ] + ) + .filter( Boolean ) + .join( '/' ); + return state.userPermissions[ key ]; }