Skip to content

Commit

Permalink
Distinguish between global and local resoluion
Browse files Browse the repository at this point in the history
  • Loading branch information
adamziel committed Feb 15, 2022
1 parent 0db7bd2 commit cf30c5a
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 1 deletion.
38 changes: 38 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 @@ -59,6 +59,44 @@ describe( 'useEntityRecord', () => {
jest.advanceTimersByTime( 1 );
} );

expect( data ).toEqual( {
status: 'SUCCESS',
isResolving: false,
hasResolved: true,
canCreate: true,
} );
} );

it( 'retrieves the relevant permissions for a resource with a key', async () => {
triggerFetch.mockImplementation( () => ( {
headers: {
Allow: 'POST',
},
} ) );
let data;
const TestComponent = () => {
data = useResourcePermissions( 'widgets', 1 );
return <div />;
};
render(
<RegistryProvider value={ registry }>
<TestComponent />
</RegistryProvider>
);
expect( data ).toEqual( {
status: 'IDLE',
isResolving: false,
hasResolved: false,
canCreate: false,
canUpdate: false,
canDelete: false,
} );

// Required to make sure no updates happen outside of act()
await act( async () => {
jest.advanceTimersByTime( 1 );
} );

expect( data ).toEqual( {
status: 'SUCCESS',
isResolving: false,
Expand Down
68 changes: 67 additions & 1 deletion packages/core-data/src/hooks/use-resource-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,73 @@ import { store as coreStore } from '../';
import { Status } from './constants';
import useQuerySelect from './use-query-select';

export default function __experimentalUseResourcePermissions( resource, id ) {
interface GlobalResourcePermissionsResolution {
/** Can the current user create new resources of this type? */
canCreate: boolean;
}
interface SpecificResourcePermissionsResolution {
/** Can the current user update resources of this type? */
canUpdate: boolean;
/** Can the current user delete resources of this type? */
canDelete: boolean;
}
interface ResolutionDetails {
/** Resolution status */
status: Status;
/**
* Is the data still being resolved?
*/
isResolving: boolean;
/**
* Is the data resolved by now?
*/
hasResolved: boolean;
}

type ResourcePermissionsResolution< IdType > = ResolutionDetails &
GlobalResourcePermissionsResolution &
( IdType extends void ? SpecificResourcePermissionsResolution : {} );

/**
* Resolves resource permissions.
*
* @param resource The resource in question, e.g. media.
* @param id ID of a specific resource entry, if needed, e.g. 10.
*
* @example
* ```js
* import { useResourcePermissions } from '@wordpress/core-data';
*
* function PagesList() {
* const { canCreate, isResolving } = useResourcePermissions( 'pages' );
*
* if ( isResolving ) {
* return 'Loading ...';
* }
*
* return (
* <div>
* {canCreate ? (<button>+ Create a new page</button>) : false}
* // ...
* </div>
* );
* }
*
* // Rendered in the application:
* // <PagesList />
* ```
*
* In the above example, when `PagesList` is rendered into an
* application, the appropriate permissions and the resolution details will be retrieved from
* the store state using `canUser()`, or resolved if missing.
*
* @return {ResourcePermissionsResolution<IdType>} Entity records data.
* @template IdType
*/
export default function __experimentalUseResourcePermissions< IdType = void >(
resource: string,
id: IdType
) {
return useQuerySelect(
( resolve ) => {
const { canUser } = resolve( coreStore );
Expand Down

0 comments on commit cf30c5a

Please sign in to comment.