Skip to content

Commit

Permalink
Add __experimentalUseResourcePermissions (#38785)
Browse files Browse the repository at this point in the history
Add useResourcePermissions hook that makes it easy to fetch user permissions:

const [ hasResolved, { canCreate, canUpdate, canDelete } ] = __experimentalUseResourcePermissions( 'navigation', ref );

It returns a tuple with hasResolved as the first item to make it intentionally hard to overlook. 

Co-authored-by: Kai Hao <[email protected]>
  • Loading branch information
adamziel and kevin940726 authored Jun 16, 2022
1 parent 6acad24 commit d3157c7
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 97 deletions.
23 changes: 14 additions & 9 deletions packages/block-library/src/navigation/test/use-navigation-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ describe( 'useNavigationMenus', () => {
it( 'Should return no information when no data is resolved', () => {
expect( useNavigationMenu() ).toEqual( {
navigationMenus: null,
navigationMenu: undefined,
canSwitchNavigationMenu: false,
canUserCreateNavigationMenu: false,
canUserDeleteNavigationMenu: false,
canUserUpdateNavigationMenu: false,
canUserDeleteNavigationMenu: undefined,
canUserUpdateNavigationMenu: undefined,
hasResolvedCanUserCreateNavigationMenu: false,
hasResolvedCanUserDeleteNavigationMenu: false,
hasResolvedCanUserUpdateNavigationMenu: false,
Expand All @@ -109,13 +110,14 @@ describe( 'useNavigationMenus', () => {
resolveCreatePermission( registry, true );
expect( useNavigationMenu() ).toEqual( {
navigationMenus,
navigationMenu: undefined,
canSwitchNavigationMenu: true,
canUserCreateNavigationMenu: true,
canUserDeleteNavigationMenu: false,
canUserUpdateNavigationMenu: false,
canUserDeleteNavigationMenu: undefined,
canUserUpdateNavigationMenu: undefined,
hasResolvedCanUserCreateNavigationMenu: true,
hasResolvedCanUserDeleteNavigationMenu: false,
hasResolvedCanUserUpdateNavigationMenu: false,
hasResolvedCanUserDeleteNavigationMenu: true,
hasResolvedCanUserUpdateNavigationMenu: true,
hasResolvedNavigationMenus: true,
isNavigationMenuMissing: true,
isNavigationMenuResolved: false,
Expand Down Expand Up @@ -170,6 +172,7 @@ describe( 'useNavigationMenus', () => {
resolveRecords( registry, navigationMenus );
resolveCreatePermission( registry, true );
resolveUpdatePermission( registry, 1, true );
resolveDeletePermission( registry, 1, false );
expect( useNavigationMenu( 1 ) ).toEqual( {
navigationMenu: navigationMenu1,
navigationMenus,
Expand All @@ -178,7 +181,7 @@ describe( 'useNavigationMenus', () => {
canUserDeleteNavigationMenu: false,
canUserUpdateNavigationMenu: true,
hasResolvedCanUserCreateNavigationMenu: true,
hasResolvedCanUserDeleteNavigationMenu: false,
hasResolvedCanUserDeleteNavigationMenu: true,
hasResolvedCanUserUpdateNavigationMenu: true,
hasResolvedNavigationMenus: true,
isNavigationMenuMissing: false,
Expand All @@ -190,6 +193,8 @@ describe( 'useNavigationMenus', () => {

it( 'Should return correct permissions (delete only)', () => {
resolveRecords( registry, navigationMenus );
resolveCreatePermission( registry, false );
resolveUpdatePermission( registry, 1, false );
resolveDeletePermission( registry, 1, true );
expect( useNavigationMenu( 1 ) ).toEqual( {
navigationMenu: navigationMenu1,
Expand All @@ -198,9 +203,9 @@ describe( 'useNavigationMenus', () => {
canUserCreateNavigationMenu: false,
canUserDeleteNavigationMenu: true,
canUserUpdateNavigationMenu: false,
hasResolvedCanUserCreateNavigationMenu: false,
hasResolvedCanUserCreateNavigationMenu: true,
hasResolvedCanUserDeleteNavigationMenu: true,
hasResolvedCanUserUpdateNavigationMenu: false,
hasResolvedCanUserUpdateNavigationMenu: true,
hasResolvedNavigationMenus: true,
isNavigationMenuMissing: false,
isNavigationMenuResolved: false,
Expand Down
105 changes: 22 additions & 83 deletions packages/block-library/src/navigation/use-navigation-menu.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
/**
* WordPress dependencies
*/
import { store as coreStore } from '@wordpress/core-data';
import {
store as coreStore,
__experimentalUseResourcePermissions as useResourcePermissions,
} from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';

export default function useNavigationMenu( ref ) {
const permissions = useResourcePermissions( 'navigation', ref );

return useSelect(
( select ) => {
const [
hasResolvedPermissions,
{ canCreate, canUpdate, canDelete, isResolving },
] = permissions;

const {
navigationMenus,
isResolvingNavigationMenus,
Expand All @@ -19,22 +29,6 @@ export default function useNavigationMenu( ref ) {
isNavigationMenuMissing,
} = selectExistingMenu( select, ref );

const {
canUserCreateNavigationMenu,
isResolvingCanUserCreateNavigationMenu,
hasResolvedCanUserCreateNavigationMenu,
} = selectMenuCreatePermissions( select );

const {
canUserUpdateNavigationMenu,
hasResolvedCanUserUpdateNavigationMenu,
} = selectMenuUpdatePermissions( select, ref );

const {
canUserDeleteNavigationMenu,
hasResolvedCanUserDeleteNavigationMenu,
} = selectMenuDeletePermissions( select, ref );

return {
navigationMenus,
isResolvingNavigationMenus,
Expand All @@ -44,22 +38,22 @@ export default function useNavigationMenu( ref ) {
isNavigationMenuResolved,
isNavigationMenuMissing,

canUserCreateNavigationMenu,
isResolvingCanUserCreateNavigationMenu,
hasResolvedCanUserCreateNavigationMenu,

canUserUpdateNavigationMenu,
hasResolvedCanUserUpdateNavigationMenu,

canUserDeleteNavigationMenu,
hasResolvedCanUserDeleteNavigationMenu,

canSwitchNavigationMenu: ref
? navigationMenus?.length > 1
: navigationMenus?.length > 0,

canUserCreateNavigationMenu: canCreate,
isResolvingCanUserCreateNavigationMenu: isResolving,
hasResolvedCanUserCreateNavigationMenu: hasResolvedPermissions,

canUserUpdateNavigationMenu: canUpdate,
hasResolvedCanUserUpdateNavigationMenu: hasResolvedPermissions,

canUserDeleteNavigationMenu: canDelete,
hasResolvedCanUserDeleteNavigationMenu: hasResolvedPermissions,
};
},
[ ref ]
[ ref, permissions ]
);
}

Expand Down Expand Up @@ -113,58 +107,3 @@ function selectExistingMenu( select, ref ) {
: null,
};
}

function selectMenuCreatePermissions( select ) {
const { hasFinishedResolution, isResolving, canUser } = select( coreStore );

const args = [ 'create', 'navigation' ];
return {
canUserCreateNavigationMenu: !! canUser( ...args ),
isResolvingCanUserCreateNavigationMenu: !! isResolving(
'canUser',
args
),
hasResolvedCanUserCreateNavigationMenu: !! hasFinishedResolution(
'canUser',
args
),
};
}

function selectMenuUpdatePermissions( select, ref ) {
if ( ! ref ) {
return {
canUserUpdateNavigationMenu: false,
hasResolvedCanUserUpdateNavigationMenu: false,
};
}

const { hasFinishedResolution, canUser } = select( coreStore );
const args = [ 'update', 'navigation', ref ];
return {
canUserUpdateNavigationMenu: !! canUser( ...args ),
hasResolvedCanUserUpdateNavigationMenu: !! hasFinishedResolution(
'canUser',
args
),
};
}

function selectMenuDeletePermissions( select, ref ) {
if ( ! ref ) {
return {
canUserDeleteNavigationMenu: false,
hasResolvedCanUserDeleteNavigationMenu: false,
};
}

const { hasFinishedResolution, canUser } = select( coreStore );
const args = [ 'delete', 'navigation', ref ];
return {
canUserDeleteNavigationMenu: !! canUser( ...args ),
hasResolvedCanUserDeleteNavigationMenu: !! hasFinishedResolution(
'canUser',
args
),
};
}
115 changes: 115 additions & 0 deletions packages/core-data/src/hooks/test/use-resource-permissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* 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 );

triggerFetch.mockImplementation( () => ( {
headers: {
get: () => ( {
allow: 'POST',
} ),
},
} ) );
} );

afterEach( () => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
} );

it( 'retrieves the relevant permissions for a key-less resource', async () => {
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 () => {
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,
},
] );
} );
} );
Loading

0 comments on commit d3157c7

Please sign in to comment.