-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
commit c86f561 Author: Adam Zieliński <[email protected]> Date: Wed Feb 16 12:17:33 2022 +0100 Move the return type to TS commit eefec13 Author: Adam Zieliński <[email protected]> Date: Wed Feb 16 12:16:36 2022 +0100 Adjust TS types commit 12c7433 Author: Adam Zieliński <[email protected]> Date: Wed Feb 16 12:13:22 2022 +0100 Make useResourcePermissions return a tuple and make hasResolved the first item of the tuple forces the users to think about that variable and it's not easy to accidentally miss it. commit caa48a7 Author: Adam Zieliński <[email protected]> Date: Tue Feb 15 17:03:31 2022 +0100 Fix typo in the tests commit 33cdb4e Author: Adam Zieliński <[email protected]> Date: Mon Feb 14 15:05:09 2022 +0100 Expose __experimentalUseResourcePermissions as a public API commit cf30c5a Author: Adam Zieliński <[email protected]> Date: Mon Feb 14 14:58:42 2022 +0100 Distinguish between global and local resoluion commit 0db7bd2 Author: Adam Zieliński <[email protected]> Date: Mon Feb 14 14:42:36 2022 +0100 Propose useResourcePermissions hook commit 2d5e270 Author: Adam Zieliński <[email protected]> Date: Mon Feb 14 14:48:09 2022 +0100 Move the status computation inside the enriched selectors commit e7ac34e Author: Adam Zieliński <[email protected]> Date: Mon Feb 14 14:01:11 2022 +0100 Propose useEntityRecords
- Loading branch information
Showing
3 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
117 changes: 117 additions & 0 deletions
117
packages/core-data/src/hooks/test/use-resource-permissions.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import triggerFetch from '@wordpress/api-fetch'; | ||
import { createRegistry, RegistryProvider } from '@wordpress/data'; | ||
|
||
jest.mock( '@wordpress/api-fetch' ); | ||
|
||
/** | ||
* External dependencies | ||
*/ | ||
import { act, render } from '@testing-library/react'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as coreDataStore } from '../../index'; | ||
import useResourcePermissions from '../use-resource-permissions'; | ||
|
||
describe( 'useResourcePermissions', () => { | ||
let registry; | ||
beforeEach( () => { | ||
jest.useFakeTimers(); | ||
|
||
registry = createRegistry(); | ||
registry.register( coreDataStore ); | ||
} ); | ||
|
||
afterEach( () => { | ||
jest.runOnlyPendingTimers(); | ||
jest.useRealTimers(); | ||
} ); | ||
|
||
it( 'retrieves the relevant permissions for a key-less resource', async () => { | ||
triggerFetch.mockImplementation( () => ( { | ||
headers: { | ||
Allow: 'POST', | ||
}, | ||
} ) ); | ||
let data; | ||
const TestComponent = () => { | ||
data = useResourcePermissions( 'widgets' ); | ||
return <div />; | ||
}; | ||
render( | ||
<RegistryProvider value={ registry }> | ||
<TestComponent /> | ||
</RegistryProvider> | ||
); | ||
expect( data ).toEqual( [ | ||
false, | ||
{ | ||
status: 'IDLE', | ||
isResolving: false, | ||
canCreate: false, | ||
}, | ||
] ); | ||
|
||
// Required to make sure no updates happen outside of act() | ||
await act( async () => { | ||
jest.advanceTimersByTime( 1 ); | ||
} ); | ||
|
||
expect( data ).toEqual( [ | ||
true, | ||
{ | ||
status: 'SUCCESS', | ||
isResolving: false, | ||
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( [ | ||
false, | ||
{ | ||
status: 'IDLE', | ||
isResolving: 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( [ | ||
true, | ||
{ | ||
status: 'SUCCESS', | ||
isResolving: false, | ||
canCreate: true, | ||
canUpdate: false, | ||
canDelete: false, | ||
}, | ||
] ); | ||
} ); | ||
} ); |
120 changes: 120 additions & 0 deletions
120
packages/core-data/src/hooks/use-resource-permissions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as coreStore } from '../'; | ||
import { Status } from './constants'; | ||
import useQuerySelect from './use-query-select'; | ||
|
||
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? | ||
*/ | ||
type HasResolved = boolean; | ||
|
||
type ResourcePermissionsResolution< IdType > = [ | ||
HasResolved, | ||
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 Entity records data. | ||
* @template IdType | ||
*/ | ||
export default function __experimentalUseResourcePermissions< IdType = void >( | ||
resource: string, | ||
id: IdType | ||
): ResourcePermissionsResolution< IdType > { | ||
return useQuerySelect( | ||
( resolve ) => { | ||
const { canUser } = resolve( coreStore ); | ||
const create = canUser( 'create', resource ); | ||
if ( ! id ) { | ||
return [ | ||
create.hasResolved, | ||
{ | ||
status: create.status, | ||
isResolving: create.isResolving, | ||
canCreate: create.hasResolved && create.data, | ||
}, | ||
]; | ||
} | ||
|
||
const update = canUser( 'update', resource, id ); | ||
const _delete = canUser( 'delete', resource, id ); | ||
const isResolving = | ||
create.isResolving || update.isResolving || _delete.isResolving; | ||
const hasResolved = | ||
create.hasResolved && update.hasResolved && _delete.hasResolved; | ||
|
||
let status = Status.Idle; | ||
if ( isResolving ) { | ||
status = Status.Resolving; | ||
} else if ( hasResolved ) { | ||
status = Status.Success; | ||
} | ||
return [ | ||
hasResolved, | ||
{ | ||
status, | ||
isResolving, | ||
canCreate: hasResolved && create.data, | ||
canUpdate: hasResolved && update.data, | ||
canDelete: hasResolved && _delete.data, | ||
}, | ||
]; | ||
}, | ||
[ resource, id ] | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters