@wordpress/core-data 4.8.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +56 -56
- package/build/actions.js.map +1 -1
- package/build/batch/create-batch.js +1 -1
- package/build/batch/create-batch.js.map +1 -1
- package/build/batch/default-processor.js +13 -7
- package/build/batch/default-processor.js.map +1 -1
- package/build/entities.js.map +1 -1
- package/build/entity-provider.js.map +1 -1
- package/build/hooks/use-entity-record.js.map +1 -1
- package/build/hooks/use-query-select.js.map +1 -1
- package/build/hooks/use-resource-permissions.js +94 -0
- package/build/hooks/use-resource-permissions.js.map +1 -0
- package/build/index.js +28 -1
- package/build/index.js.map +1 -1
- package/build/queried-data/selectors.js.map +1 -1
- package/build/reducer.js +4 -1
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +4 -14
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +45 -8
- package/build/selectors.js.map +1 -1
- package/build/utils/forward-resolver.js.map +1 -1
- package/build/utils/on-sub-key.js.map +1 -1
- package/build/utils/with-weak-map-cache.js +1 -7
- package/build/utils/with-weak-map-cache.js.map +1 -1
- package/build-module/actions.js.map +1 -1
- package/build-module/batch/create-batch.js +2 -2
- package/build-module/batch/create-batch.js.map +1 -1
- package/build-module/batch/default-processor.js +12 -5
- package/build-module/batch/default-processor.js.map +1 -1
- package/build-module/entities.js.map +1 -1
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/hooks/use-entity-record.js.map +1 -1
- package/build-module/hooks/use-query-select.js.map +1 -1
- package/build-module/hooks/use-resource-permissions.js +82 -0
- package/build-module/hooks/use-resource-permissions.js.map +1 -0
- package/build-module/index.js +3 -0
- package/build-module/index.js.map +1 -1
- package/build-module/queried-data/selectors.js.map +1 -1
- package/build-module/reducer.js +5 -2
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +5 -15
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +40 -4
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/forward-resolver.js.map +1 -1
- package/build-module/utils/on-sub-key.js.map +1 -1
- package/build-module/utils/with-weak-map-cache.js +1 -6
- package/build-module/utils/with-weak-map-cache.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.js +389 -372
- package/src/batch/create-batch.js +2 -2
- package/src/batch/default-processor.js +10 -5
- package/src/entities.ts +16 -17
- package/src/entity-provider.js +4 -6
- package/src/entity-types/attachment.ts +4 -3
- package/src/entity-types/comment.ts +4 -3
- package/src/entity-types/entities.ts +5 -2
- package/src/entity-types/index.ts +114 -20
- package/src/entity-types/menu-location.ts +4 -3
- package/src/entity-types/nav-menu-item.ts +4 -3
- package/src/entity-types/nav-menu.ts +3 -3
- package/src/entity-types/page.ts +3 -3
- package/src/entity-types/plugin.ts +3 -3
- package/src/entity-types/post.ts +3 -3
- package/src/entity-types/settings.ts +3 -3
- package/src/entity-types/sidebar.ts +4 -3
- package/src/entity-types/taxonomy.ts +4 -3
- package/src/entity-types/theme.ts +3 -3
- package/src/entity-types/type.ts +3 -3
- package/src/entity-types/user.ts +3 -3
- package/src/entity-types/widget-type.ts +4 -3
- package/src/entity-types/widget.ts +3 -3
- package/src/entity-types/wp-template-part.ts +4 -3
- package/src/entity-types/wp-template.ts +4 -3
- package/src/fetch/test/__experimental-fetch-link-suggestions.js +2 -4
- package/src/hooks/test/use-query-select.js +4 -2
- package/src/hooks/test/use-resource-permissions.js +115 -0
- package/src/hooks/use-entity-record.ts +0 -1
- package/src/hooks/use-query-select.ts +26 -24
- package/src/hooks/use-resource-permissions.ts +120 -0
- package/src/index.js +3 -0
- package/src/locks/test/selectors.js +2 -1
- package/src/queried-data/selectors.js +2 -8
- package/src/reducer.js +9 -2
- package/src/resolvers.js +344 -326
- package/src/selectors.ts +347 -194
- package/src/test/reducer.js +5 -4
- package/src/test/resolvers.js +1 -3
- package/src/test/selectors.js +1 -2
- package/src/utils/forward-resolver.js +6 -5
- package/src/utils/on-sub-key.js +20 -20
- package/src/utils/with-weak-map-cache.js +1 -6
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import triggerFetch from '@wordpress/api-fetch';
|
|
5
|
+
import { createRegistry, RegistryProvider } from '@wordpress/data';
|
|
6
|
+
|
|
7
|
+
jest.mock( '@wordpress/api-fetch' );
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* External dependencies
|
|
11
|
+
*/
|
|
12
|
+
import { act, render } from '@testing-library/react';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Internal dependencies
|
|
16
|
+
*/
|
|
17
|
+
import { store as coreDataStore } from '../../index';
|
|
18
|
+
import useResourcePermissions from '../use-resource-permissions';
|
|
19
|
+
|
|
20
|
+
describe( 'useResourcePermissions', () => {
|
|
21
|
+
let registry;
|
|
22
|
+
beforeEach( () => {
|
|
23
|
+
jest.useFakeTimers();
|
|
24
|
+
|
|
25
|
+
registry = createRegistry();
|
|
26
|
+
registry.register( coreDataStore );
|
|
27
|
+
|
|
28
|
+
triggerFetch.mockImplementation( () => ( {
|
|
29
|
+
headers: {
|
|
30
|
+
get: () => ( {
|
|
31
|
+
allow: 'POST',
|
|
32
|
+
} ),
|
|
33
|
+
},
|
|
34
|
+
} ) );
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
afterEach( () => {
|
|
38
|
+
jest.runOnlyPendingTimers();
|
|
39
|
+
jest.useRealTimers();
|
|
40
|
+
} );
|
|
41
|
+
|
|
42
|
+
it( 'retrieves the relevant permissions for a key-less resource', async () => {
|
|
43
|
+
let data;
|
|
44
|
+
const TestComponent = () => {
|
|
45
|
+
data = useResourcePermissions( 'widgets' );
|
|
46
|
+
return <div />;
|
|
47
|
+
};
|
|
48
|
+
render(
|
|
49
|
+
<RegistryProvider value={ registry }>
|
|
50
|
+
<TestComponent />
|
|
51
|
+
</RegistryProvider>
|
|
52
|
+
);
|
|
53
|
+
expect( data ).toEqual( [
|
|
54
|
+
false,
|
|
55
|
+
{
|
|
56
|
+
status: 'IDLE',
|
|
57
|
+
isResolving: false,
|
|
58
|
+
canCreate: false,
|
|
59
|
+
},
|
|
60
|
+
] );
|
|
61
|
+
|
|
62
|
+
// Required to make sure no updates happen outside of act()
|
|
63
|
+
await act( async () => {
|
|
64
|
+
jest.advanceTimersByTime( 1 );
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
expect( data ).toEqual( [
|
|
68
|
+
true,
|
|
69
|
+
{
|
|
70
|
+
status: 'SUCCESS',
|
|
71
|
+
isResolving: false,
|
|
72
|
+
canCreate: true,
|
|
73
|
+
},
|
|
74
|
+
] );
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
it( 'retrieves the relevant permissions for a resource with a key', async () => {
|
|
78
|
+
let data;
|
|
79
|
+
const TestComponent = () => {
|
|
80
|
+
data = useResourcePermissions( 'widgets', 1 );
|
|
81
|
+
return <div />;
|
|
82
|
+
};
|
|
83
|
+
render(
|
|
84
|
+
<RegistryProvider value={ registry }>
|
|
85
|
+
<TestComponent />
|
|
86
|
+
</RegistryProvider>
|
|
87
|
+
);
|
|
88
|
+
expect( data ).toEqual( [
|
|
89
|
+
false,
|
|
90
|
+
{
|
|
91
|
+
status: 'IDLE',
|
|
92
|
+
isResolving: false,
|
|
93
|
+
canCreate: false,
|
|
94
|
+
canUpdate: false,
|
|
95
|
+
canDelete: false,
|
|
96
|
+
},
|
|
97
|
+
] );
|
|
98
|
+
|
|
99
|
+
// Required to make sure no updates happen outside of act()
|
|
100
|
+
await act( async () => {
|
|
101
|
+
jest.advanceTimersByTime( 1 );
|
|
102
|
+
} );
|
|
103
|
+
|
|
104
|
+
expect( data ).toEqual( [
|
|
105
|
+
true,
|
|
106
|
+
{
|
|
107
|
+
status: 'SUCCESS',
|
|
108
|
+
isResolving: false,
|
|
109
|
+
canCreate: true,
|
|
110
|
+
canUpdate: false,
|
|
111
|
+
canDelete: false,
|
|
112
|
+
},
|
|
113
|
+
] );
|
|
114
|
+
} );
|
|
115
|
+
} );
|
|
@@ -9,7 +9,6 @@ import deprecated from '@wordpress/deprecated';
|
|
|
9
9
|
import useQuerySelect from './use-query-select';
|
|
10
10
|
import { store as coreStore } from '../';
|
|
11
11
|
import type { Status } from './constants';
|
|
12
|
-
import useEntityRecords from './use-entity-records';
|
|
13
12
|
|
|
14
13
|
export interface EntityRecordResolution< RecordType > {
|
|
15
14
|
/** The requested entity record */
|
|
@@ -102,34 +102,36 @@ const enrichSelectors = memoize( ( ( selectors ) => {
|
|
|
102
102
|
continue;
|
|
103
103
|
}
|
|
104
104
|
Object.defineProperty( resolvers, selectorName, {
|
|
105
|
-
get:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
get:
|
|
106
|
+
() =>
|
|
107
|
+
( ...args: unknown[] ) => {
|
|
108
|
+
const { getIsResolving, hasFinishedResolution } = selectors;
|
|
109
|
+
const isResolving = !! getIsResolving( selectorName, args );
|
|
110
|
+
const hasResolved =
|
|
111
|
+
! isResolving &&
|
|
112
|
+
hasFinishedResolution( selectorName, args );
|
|
113
|
+
const data = selectors[ selectorName ]( ...args );
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
let status;
|
|
116
|
+
if ( isResolving ) {
|
|
117
|
+
status = Status.Resolving;
|
|
118
|
+
} else if ( hasResolved ) {
|
|
119
|
+
if ( data ) {
|
|
120
|
+
status = Status.Success;
|
|
121
|
+
} else {
|
|
122
|
+
status = Status.Error;
|
|
123
|
+
}
|
|
119
124
|
} else {
|
|
120
|
-
status = Status.
|
|
125
|
+
status = Status.Idle;
|
|
121
126
|
}
|
|
122
|
-
} else {
|
|
123
|
-
status = Status.Idle;
|
|
124
|
-
}
|
|
125
127
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
return {
|
|
129
|
+
data,
|
|
130
|
+
status,
|
|
131
|
+
isResolving,
|
|
132
|
+
hasResolved,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
133
135
|
} );
|
|
134
136
|
}
|
|
135
137
|
return resolvers;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { store as coreStore } from '../';
|
|
5
|
+
import { Status } from './constants';
|
|
6
|
+
import useQuerySelect from './use-query-select';
|
|
7
|
+
|
|
8
|
+
interface GlobalResourcePermissionsResolution {
|
|
9
|
+
/** Can the current user create new resources of this type? */
|
|
10
|
+
canCreate: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface SpecificResourcePermissionsResolution {
|
|
13
|
+
/** Can the current user update resources of this type? */
|
|
14
|
+
canUpdate: boolean;
|
|
15
|
+
/** Can the current user delete resources of this type? */
|
|
16
|
+
canDelete: boolean;
|
|
17
|
+
}
|
|
18
|
+
interface ResolutionDetails {
|
|
19
|
+
/** Resolution status */
|
|
20
|
+
status: Status;
|
|
21
|
+
/**
|
|
22
|
+
* Is the data still being resolved?
|
|
23
|
+
*/
|
|
24
|
+
isResolving: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Is the data resolved by now?
|
|
29
|
+
*/
|
|
30
|
+
type HasResolved = boolean;
|
|
31
|
+
|
|
32
|
+
type ResourcePermissionsResolution< IdType > = [
|
|
33
|
+
HasResolved,
|
|
34
|
+
ResolutionDetails &
|
|
35
|
+
GlobalResourcePermissionsResolution &
|
|
36
|
+
( IdType extends void ? SpecificResourcePermissionsResolution : {} )
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolves resource permissions.
|
|
41
|
+
*
|
|
42
|
+
* @param resource The resource in question, e.g. media.
|
|
43
|
+
* @param id ID of a specific resource entry, if needed, e.g. 10.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```js
|
|
47
|
+
* import { useResourcePermissions } from '@wordpress/core-data';
|
|
48
|
+
*
|
|
49
|
+
* function PagesList() {
|
|
50
|
+
* const { canCreate, isResolving } = useResourcePermissions( 'pages' );
|
|
51
|
+
*
|
|
52
|
+
* if ( isResolving ) {
|
|
53
|
+
* return 'Loading ...';
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* return (
|
|
57
|
+
* <div>
|
|
58
|
+
* {canCreate ? (<button>+ Create a new page</button>) : false}
|
|
59
|
+
* // ...
|
|
60
|
+
* </div>
|
|
61
|
+
* );
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* // Rendered in the application:
|
|
65
|
+
* // <PagesList />
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* In the above example, when `PagesList` is rendered into an
|
|
69
|
+
* application, the appropriate permissions and the resolution details will be retrieved from
|
|
70
|
+
* the store state using `canUser()`, or resolved if missing.
|
|
71
|
+
*
|
|
72
|
+
* @return Entity records data.
|
|
73
|
+
* @template IdType
|
|
74
|
+
*/
|
|
75
|
+
export default function __experimentalUseResourcePermissions< IdType = void >(
|
|
76
|
+
resource: string,
|
|
77
|
+
id?: IdType
|
|
78
|
+
): ResourcePermissionsResolution< IdType > {
|
|
79
|
+
return useQuerySelect(
|
|
80
|
+
( resolve ) => {
|
|
81
|
+
const { canUser } = resolve( coreStore );
|
|
82
|
+
const create = canUser( 'create', resource );
|
|
83
|
+
if ( ! id ) {
|
|
84
|
+
return [
|
|
85
|
+
create.hasResolved,
|
|
86
|
+
{
|
|
87
|
+
status: create.status,
|
|
88
|
+
isResolving: create.isResolving,
|
|
89
|
+
canCreate: create.hasResolved && create.data,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const update = canUser( 'update', resource, id );
|
|
95
|
+
const _delete = canUser( 'delete', resource, id );
|
|
96
|
+
const isResolving =
|
|
97
|
+
create.isResolving || update.isResolving || _delete.isResolving;
|
|
98
|
+
const hasResolved =
|
|
99
|
+
create.hasResolved && update.hasResolved && _delete.hasResolved;
|
|
100
|
+
|
|
101
|
+
let status = Status.Idle;
|
|
102
|
+
if ( isResolving ) {
|
|
103
|
+
status = Status.Resolving;
|
|
104
|
+
} else if ( hasResolved ) {
|
|
105
|
+
status = Status.Success;
|
|
106
|
+
}
|
|
107
|
+
return [
|
|
108
|
+
hasResolved,
|
|
109
|
+
{
|
|
110
|
+
status,
|
|
111
|
+
isResolving,
|
|
112
|
+
canCreate: hasResolved && create.data,
|
|
113
|
+
canUpdate: hasResolved && update.data,
|
|
114
|
+
canDelete: hasResolved && _delete.data,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
},
|
|
118
|
+
[ resource, id ]
|
|
119
|
+
);
|
|
120
|
+
}
|
package/src/index.js
CHANGED
|
@@ -68,6 +68,9 @@ export const store = createReduxStore( STORE_NAME, storeConfig() );
|
|
|
68
68
|
register( store );
|
|
69
69
|
|
|
70
70
|
export { default as EntityProvider } from './entity-provider';
|
|
71
|
+
export { default as useEntityRecord } from './hooks/use-entity-record';
|
|
72
|
+
export { default as useEntityRecords } from './hooks/use-entity-records';
|
|
73
|
+
export { default as __experimentalUseResourcePermissions } from './hooks/use-resource-permissions';
|
|
71
74
|
export * from './entity-provider';
|
|
72
75
|
export * from './entity-types';
|
|
73
76
|
export * from './fetch';
|
|
@@ -28,14 +28,8 @@ const queriedItemsCacheByState = new WeakMap();
|
|
|
28
28
|
* @return {?Array} Query items.
|
|
29
29
|
*/
|
|
30
30
|
function getQueriedItemsUncached( state, query ) {
|
|
31
|
-
const {
|
|
32
|
-
|
|
33
|
-
page,
|
|
34
|
-
perPage,
|
|
35
|
-
include,
|
|
36
|
-
fields,
|
|
37
|
-
context,
|
|
38
|
-
} = getQueryParts( query );
|
|
31
|
+
const { stableKey, page, perPage, include, fields, context } =
|
|
32
|
+
getQueryParts( query );
|
|
39
33
|
let itemIds;
|
|
40
34
|
|
|
41
35
|
if ( state.queries?.[ context ]?.[ stableKey ] ) {
|
package/src/reducer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { map, groupBy, flowRight, isEqual, get } from 'lodash';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
@@ -55,7 +55,14 @@ export function users( state = { byId: {}, queries: {} }, action ) {
|
|
|
55
55
|
return {
|
|
56
56
|
byId: {
|
|
57
57
|
...state.byId,
|
|
58
|
-
|
|
58
|
+
// Key users by their ID.
|
|
59
|
+
...action.users.reduce(
|
|
60
|
+
( newUsers, user ) => ( {
|
|
61
|
+
...newUsers,
|
|
62
|
+
[ user.id ]: user,
|
|
63
|
+
} ),
|
|
64
|
+
{}
|
|
65
|
+
),
|
|
59
66
|
},
|
|
60
67
|
queries: {
|
|
61
68
|
...state.queries,
|