Skip to content

Commit

Permalink
Code Data: Support entity queries in the 'useResourcePermissions' hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Mamaduka committed Jul 17, 2024
1 parent 1eff14a commit f45b6b8
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
15 changes: 11 additions & 4 deletions packages/core-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ...';
Expand All @@ -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 ...';
Expand All @@ -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_

Expand Down
71 changes: 71 additions & 0 deletions packages/core-data/src/hooks/test/use-resource-permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <div />;
};
render(
<RegistryProvider value={ registry }>
<TestComponent />
</RegistryProvider>
);
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 <div />;
};
render(
<RegistryProvider value={ registry }>
<TestComponent />
</RegistryProvider>
);
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,
} )
);
} );
} );
27 changes: 20 additions & 7 deletions packages/core-data/src/hooks/use-resource-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/
import deprecated from '@wordpress/deprecated';
import { addQueryArgs } from '@wordpress/url';

/**
* Internal dependencies
Expand Down Expand Up @@ -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 ...';
Expand Down Expand Up @@ -82,7 +86,7 @@ type ResourcePermissionsResolution< IdType > = [
* canUpdate,
* canDelete,
* isResolving
* } = useResourcePermissions( 'pages', pageId );
* } = useResourcePermissions( { kind: 'postType', name: 'page', id: pageId } );
*
* if ( isResolving ) {
* return 'Loading ...';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -168,7 +181,7 @@ export default function useResourcePermissions< IdType = void >(
canDelete: hasResolved && _delete.data,
};
},
[ resource, id ]
[ resourceAsString, id ]
);
}

Expand Down

0 comments on commit f45b6b8

Please sign in to comment.