From f45b6b8c7cca066f9c7865f7d8cf1ea6f5b927a6 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 17 Jul 2024 15:03:22 +0400 Subject: [PATCH] Code Data: Support entity queries in the 'useResourcePermissions' hook --- packages/core-data/README.md | 15 ++-- .../hooks/test/use-resource-permissions.js | 71 +++++++++++++++++++ .../src/hooks/use-resource-permissions.ts | 27 +++++-- 3 files changed, 102 insertions(+), 11 deletions(-) diff --git a/packages/core-data/README.md b/packages/core-data/README.md index 8c262ebeee8d19..f30a620c4a31ca 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -1132,7 +1132,10 @@ _Usage_ import { useResourcePermissions } from '@wordpress/core-data'; function PagesList() { - const { canCreate, isResolving } = useResourcePermissions( 'pages' ); + const { canCreate, isResolving } = useResourcePermissions( { + kind: 'postType', + name: 'page', + } ); if ( isResolving ) { return 'Loading ...'; @@ -1155,7 +1158,11 @@ import { useResourcePermissions } from '@wordpress/core-data'; function Page( { pageId } ) { const { canCreate, canUpdate, canDelete, isResolving } = - useResourcePermissions( 'pages', pageId ); + useResourcePermissions( { + kind: 'postType', + name: 'page', + id: pageId, + } ); if ( isResolving ) { return 'Loading ...'; @@ -1181,8 +1188,8 @@ the store state using `canUser()`, or resolved if missing. _Parameters_ -- _resource_ `string`: The resource in question, e.g. media. -- _id_ `IdType`: ID of a specific resource entry, if needed, e.g. 10. +- _resource_ `string | EntityResource`: Entity resource to check. Accepts entity object `{ kind: 'root', name: 'media', id: 1 }` or REST base as a string - `media`. +- _id_ `IdType`: Optional ID of the rest resource to check. e.g. 10. _Returns_ diff --git a/packages/core-data/src/hooks/test/use-resource-permissions.js b/packages/core-data/src/hooks/test/use-resource-permissions.js index bb6c3a85c191f6..1ef35cab3353c9 100644 --- a/packages/core-data/src/hooks/test/use-resource-permissions.js +++ b/packages/core-data/src/hooks/test/use-resource-permissions.js @@ -93,4 +93,75 @@ describe( 'useResourcePermissions', () => { } ) ); } ); + + it( 'retrieves the relevant permissions for a id-less entity', async () => { + let data; + const TestComponent = () => { + data = useResourcePermissions( { + kind: 'root', + name: 'media', + } ); + return
; + }; + render( + + + + ); + expect( data ).toEqual( { + status: 'IDLE', + isResolving: false, + hasResolved: false, + canCreate: false, + canRead: false, + } ); + + await waitFor( () => + expect( data ).toEqual( { + status: 'SUCCESS', + isResolving: false, + hasResolved: true, + canCreate: true, + canRead: false, + } ) + ); + } ); + + it( 'retrieves the relevant permissions for an entity', async () => { + let data; + const TestComponent = () => { + data = useResourcePermissions( { + kind: 'root', + name: 'media', + id: 1, + } ); + return
; + }; + render( + + + + ); + expect( data ).toEqual( { + status: 'IDLE', + isResolving: false, + hasResolved: false, + canCreate: false, + canRead: false, + canUpdate: false, + canDelete: false, + } ); + + await waitFor( () => + expect( data ).toEqual( { + status: 'SUCCESS', + isResolving: false, + hasResolved: true, + canCreate: true, + canRead: false, + canUpdate: false, + canDelete: false, + } ) + ); + } ); } ); diff --git a/packages/core-data/src/hooks/use-resource-permissions.ts b/packages/core-data/src/hooks/use-resource-permissions.ts index 7da7189e2e5067..1f97ed5b45aa5e 100644 --- a/packages/core-data/src/hooks/use-resource-permissions.ts +++ b/packages/core-data/src/hooks/use-resource-permissions.ts @@ -2,6 +2,7 @@ * WordPress dependencies */ import deprecated from '@wordpress/deprecated'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -41,20 +42,23 @@ type ResourcePermissionsResolution< IdType > = [ ( IdType extends void ? SpecificResourcePermissionsResolution : {} ), ]; +type EntityResource = { kind: string; name: string; id?: string | number }; + /** * Resolves resource permissions. * * @since 6.1.0 Introduced in WordPress core. * - * @param resource The resource in question, e.g. media. - * @param id ID of a specific resource entry, if needed, e.g. 10. + * @param resource Entity resource to check. Accepts entity object `{ kind: 'root', name: 'media', id: 1 }` + * or REST base as a string - `media`. + * @param id Optional ID of the rest resource to check. e.g. 10. * * @example * ```js * import { useResourcePermissions } from '@wordpress/core-data'; * * function PagesList() { - * const { canCreate, isResolving } = useResourcePermissions( 'pages' ); + * const { canCreate, isResolving } = useResourcePermissions( { kind: 'postType', name: 'page' } ); * * if ( isResolving ) { * return 'Loading ...'; @@ -82,7 +86,7 @@ type ResourcePermissionsResolution< IdType > = [ * canUpdate, * canDelete, * isResolving - * } = useResourcePermissions( 'pages', pageId ); + * } = useResourcePermissions( { kind: 'postType', name: 'page', id: pageId } ); * * if ( isResolving ) { * return 'Loading ...'; @@ -110,14 +114,23 @@ type ResourcePermissionsResolution< IdType > = [ * @template IdType */ export default function useResourcePermissions< IdType = void >( - resource: string, + resource: string | EntityResource, id?: IdType ): ResourcePermissionsResolution< IdType > { + // Serialize `resource` to a string that can be safely used as a React dep. + // We can't just pass `resource` as one of the deps, because if it is passed + // as an object literal, then it will be a different object on each call even + // if the values remain the same. + const resourceAsString = + typeof resource === 'object' ? addQueryArgs( '', resource ) : resource; + return useQuerySelect( ( resolve ) => { const { canUser } = resolve( coreStore ); const create = canUser( 'create', resource ); - if ( ! id ) { + + const hasId = typeof resource === 'object' ? !! resource.id : !! id; + if ( ! hasId ) { const read = canUser( 'read', resource ); const isResolving = create.isResolving || read.isResolving; @@ -168,7 +181,7 @@ export default function useResourcePermissions< IdType = void >( canDelete: hasResolved && _delete.data, }; }, - [ resource, id ] + [ resourceAsString, id ] ); }