@wordpress/core-data 7.2.0 → 7.4.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 +4 -0
- package/README.md +54 -6
- package/build/actions.js +6 -6
- package/build/actions.js.map +1 -1
- package/build/entity-context.js +13 -0
- package/build/entity-context.js.map +1 -0
- package/build/entity-provider.js +4 -189
- package/build/entity-provider.js.map +1 -1
- package/build/entity-types/menu-location.js.map +1 -1
- package/build/entity-types/settings.js.map +1 -1
- package/build/entity-types/theme.js.map +1 -1
- package/build/entity-types/widget-type.js.map +1 -1
- package/build/hooks/index.js +22 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use-entity-block-editor.js +140 -0
- package/build/hooks/use-entity-block-editor.js.map +1 -0
- package/build/hooks/use-entity-id.js +28 -0
- package/build/hooks/use-entity-id.js.map +1 -0
- package/build/hooks/use-entity-prop.js +65 -0
- package/build/hooks/use-entity-prop.js.map +1 -0
- package/build/hooks/use-resource-permissions.js +25 -8
- package/build/hooks/use-resource-permissions.js.map +1 -1
- package/build/resolvers.js +81 -70
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +23 -9
- package/build/selectors.js.map +1 -1
- package/build/utils/index.js +19 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/user-permissions.js +32 -0
- package/build/utils/user-permissions.js.map +1 -0
- package/build-module/actions.js +6 -6
- package/build-module/actions.js.map +1 -1
- package/build-module/entity-context.js +6 -0
- package/build-module/entity-context.js.map +1 -0
- package/build-module/entity-provider.js +3 -185
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/entity-types/menu-location.js.map +1 -1
- package/build-module/entity-types/settings.js.map +1 -1
- package/build-module/entity-types/theme.js.map +1 -1
- package/build-module/entity-types/widget-type.js.map +1 -1
- package/build-module/hooks/index.js +3 -0
- package/build-module/hooks/index.js.map +1 -1
- package/build-module/hooks/use-entity-block-editor.js +132 -0
- package/build-module/hooks/use-entity-block-editor.js.map +1 -0
- package/build-module/hooks/use-entity-id.js +22 -0
- package/build-module/hooks/use-entity-id.js.map +1 -0
- package/build-module/hooks/use-entity-prop.js +58 -0
- package/build-module/hooks/use-entity-prop.js.map +1 -0
- package/build-module/hooks/use-resource-permissions.js +25 -8
- package/build-module/hooks/use-resource-permissions.js.map +1 -1
- package/build-module/resolvers.js +82 -71
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +24 -10
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/index.js +1 -0
- package/build-module/utils/index.js.map +1 -1
- package/build-module/utils/user-permissions.js +24 -0
- package/build-module/utils/user-permissions.js.map +1 -0
- package/build-types/actions.d.ts +3 -3
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/batch/create-batch.d.ts.map +1 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/entity-context.d.ts +2 -0
- package/build-types/entity-context.d.ts.map +1 -0
- package/build-types/entity-provider.d.ts +0 -48
- package/build-types/entity-provider.d.ts.map +1 -1
- package/build-types/entity-types/menu-location.d.ts.map +1 -1
- package/build-types/entity-types/settings.d.ts.map +1 -1
- package/build-types/entity-types/theme.d.ts.map +1 -1
- package/build-types/entity-types/widget-type.d.ts.map +1 -1
- package/build-types/fetch/__experimental-fetch-url-data.d.ts.map +1 -1
- package/build-types/hooks/index.d.ts +3 -0
- package/build-types/hooks/index.d.ts.map +1 -1
- package/build-types/hooks/use-entity-block-editor.d.ts +22 -0
- package/build-types/hooks/use-entity-block-editor.d.ts.map +1 -0
- package/build-types/hooks/use-entity-id.d.ts +9 -0
- package/build-types/hooks/use-entity-id.d.ts.map +1 -0
- package/build-types/hooks/use-entity-prop.d.ts +19 -0
- package/build-types/hooks/use-entity-prop.d.ts.map +1 -0
- package/build-types/hooks/use-resource-permissions.d.ts +8 -70
- package/build-types/hooks/use-resource-permissions.d.ts.map +1 -1
- package/build-types/index.d.ts +35 -32
- package/build-types/index.d.ts.map +1 -1
- package/build-types/locks/reducer.d.ts +1 -1
- package/build-types/locks/reducer.d.ts.map +1 -1
- package/build-types/queried-data/actions.d.ts +1 -1
- package/build-types/queried-data/actions.d.ts.map +1 -1
- package/build-types/queried-data/get-query-parts.d.ts.map +1 -1
- package/build-types/queried-data/reducer.d.ts +1 -1
- package/build-types/queried-data/reducer.d.ts.map +1 -1
- package/build-types/queried-data/selectors.d.ts +0 -1
- package/build-types/queried-data/selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +13 -13
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +3 -2
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +11 -6
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/get-nested-value.d.ts.map +1 -1
- package/build-types/utils/get-normalized-comma-separable.d.ts.map +1 -1
- package/build-types/utils/if-matching-action.d.ts +1 -1
- package/build-types/utils/index.d.ts +1 -0
- package/build-types/utils/on-sub-key.d.ts +1 -1
- package/build-types/utils/replace-action.d.ts +1 -1
- package/build-types/utils/set-nested-value.d.ts.map +1 -1
- package/build-types/utils/user-permissions.d.ts +4 -0
- package/build-types/utils/user-permissions.d.ts.map +1 -0
- package/package.json +18 -17
- package/src/actions.js +6 -6
- package/src/entity-context.js +6 -0
- package/src/entity-provider.js +2 -211
- package/src/entity-types/menu-location.ts +1 -0
- package/src/entity-types/settings.ts +1 -0
- package/src/entity-types/theme.ts +1 -0
- package/src/entity-types/widget-type.ts +1 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/test/use-entity-record.js +5 -3
- package/src/hooks/test/use-resource-permissions.js +96 -5
- package/src/hooks/use-entity-block-editor.js +148 -0
- package/src/hooks/use-entity-id.js +21 -0
- package/src/hooks/use-entity-prop.js +60 -0
- package/src/hooks/use-resource-permissions.ts +46 -9
- package/src/resolvers.js +102 -78
- package/src/selectors.ts +24 -9
- package/src/test/entity-provider.js +6 -2
- package/src/test/resolvers.js +221 -50
- package/src/test/selectors.js +18 -55
- package/src/utils/index.js +5 -0
- package/src/utils/user-permissions.js +39 -0
- package/tsconfig.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
package/src/selectors.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
isRawAttribute,
|
|
21
21
|
setNestedValue,
|
|
22
22
|
isNumericID,
|
|
23
|
+
getUserPermissionCacheKey,
|
|
23
24
|
} from './utils';
|
|
24
25
|
import type * as ET from './entity-types';
|
|
25
26
|
import type { UndoManager } from '@wordpress/undo-manager';
|
|
@@ -120,6 +121,8 @@ type EntityRecordArgs =
|
|
|
120
121
|
| [ string, string, EntityRecordKey ]
|
|
121
122
|
| [ string, string, EntityRecordKey, GetRecordsHttpQuery ];
|
|
122
123
|
|
|
124
|
+
type EntityResource = { kind: string; name: string; id?: EntityRecordKey };
|
|
125
|
+
|
|
123
126
|
/**
|
|
124
127
|
* Shared reference to an empty object for cases where it is important to avoid
|
|
125
128
|
* returning a new object reference on every invocation, as in a connected or
|
|
@@ -232,7 +235,9 @@ export function getEntitiesByKind( state: State, kind: string ): Array< any > {
|
|
|
232
235
|
export const getEntitiesConfig = createSelector(
|
|
233
236
|
( state: State, kind: string ): Array< any > =>
|
|
234
237
|
state.entities.config.filter( ( entity ) => entity.kind === kind ),
|
|
238
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
235
239
|
( state: State, kind: string ) => state.entities.config
|
|
240
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
236
241
|
);
|
|
237
242
|
/**
|
|
238
243
|
* Returns the entity config given its kind and name.
|
|
@@ -992,6 +997,7 @@ export function getLastEntityDeleteError(
|
|
|
992
997
|
?.error;
|
|
993
998
|
}
|
|
994
999
|
|
|
1000
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
995
1001
|
/**
|
|
996
1002
|
* Returns the previous edit from the current undo offset
|
|
997
1003
|
* for the entity records edits history, if any.
|
|
@@ -1008,7 +1014,9 @@ export function getUndoEdit( state: State ): Optional< any > {
|
|
|
1008
1014
|
} );
|
|
1009
1015
|
return undefined;
|
|
1010
1016
|
}
|
|
1017
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
1011
1018
|
|
|
1019
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1012
1020
|
/**
|
|
1013
1021
|
* Returns the next edit from the current undo offset
|
|
1014
1022
|
* for the entity records edits history, if any.
|
|
@@ -1025,6 +1033,7 @@ export function getRedoEdit( state: State ): Optional< any > {
|
|
|
1025
1033
|
} );
|
|
1026
1034
|
return undefined;
|
|
1027
1035
|
}
|
|
1036
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
1028
1037
|
|
|
1029
1038
|
/**
|
|
1030
1039
|
* Returns true if there is a previous edit from the current undo offset
|
|
@@ -1130,7 +1139,8 @@ export function isPreviewEmbedFallback( state: State, url: string ): boolean {
|
|
|
1130
1139
|
*
|
|
1131
1140
|
* @param state Data state.
|
|
1132
1141
|
* @param action Action to check. One of: 'create', 'read', 'update', 'delete'.
|
|
1133
|
-
* @param resource
|
|
1142
|
+
* @param resource Entity resource to check. Accepts entity object `{ kind: 'root', name: 'media', id: 1 }`
|
|
1143
|
+
* or REST base as a string - `media`.
|
|
1134
1144
|
* @param id Optional ID of the rest resource to check.
|
|
1135
1145
|
*
|
|
1136
1146
|
* @return Whether or not the user can perform the action,
|
|
@@ -1139,10 +1149,16 @@ export function isPreviewEmbedFallback( state: State, url: string ): boolean {
|
|
|
1139
1149
|
export function canUser(
|
|
1140
1150
|
state: State,
|
|
1141
1151
|
action: string,
|
|
1142
|
-
resource: string,
|
|
1152
|
+
resource: string | EntityResource,
|
|
1143
1153
|
id?: EntityRecordKey
|
|
1144
1154
|
): boolean | undefined {
|
|
1145
|
-
const
|
|
1155
|
+
const isEntity = typeof resource === 'object';
|
|
1156
|
+
if ( isEntity && ( ! resource.kind || ! resource.name ) ) {
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const key = getUserPermissionCacheKey( action, resource, id );
|
|
1161
|
+
|
|
1146
1162
|
return state.userPermissions[ key ];
|
|
1147
1163
|
}
|
|
1148
1164
|
|
|
@@ -1167,13 +1183,12 @@ export function canUserEditEntityRecord(
|
|
|
1167
1183
|
name: string,
|
|
1168
1184
|
recordId: EntityRecordKey
|
|
1169
1185
|
): boolean | undefined {
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
}
|
|
1174
|
-
const resource = entityConfig.__unstable_rest_base;
|
|
1186
|
+
deprecated( `wp.data.select( 'core' ).canUserEditEntityRecord()`, {
|
|
1187
|
+
since: '6.7',
|
|
1188
|
+
alternative: `wp.data.select( 'core' ).canUser( 'update', { kind, name, id } )`,
|
|
1189
|
+
} );
|
|
1175
1190
|
|
|
1176
|
-
return canUser( state, 'update',
|
|
1191
|
+
return canUser( state, 'update', { kind, name, id: recordId } );
|
|
1177
1192
|
}
|
|
1178
1193
|
|
|
1179
1194
|
/**
|
|
@@ -14,13 +14,14 @@ import {
|
|
|
14
14
|
} from '@wordpress/blocks';
|
|
15
15
|
import { RichText, useBlockProps } from '@wordpress/block-editor';
|
|
16
16
|
import { createRegistry, RegistryProvider } from '@wordpress/data';
|
|
17
|
-
import '@wordpress/block-library';
|
|
17
|
+
import { registerCoreBlocks } from '@wordpress/block-library';
|
|
18
|
+
import { unregisterFormatType } from '@wordpress/rich-text';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Internal dependencies
|
|
21
22
|
*/
|
|
22
23
|
import { store as coreDataStore } from '../index';
|
|
23
|
-
import
|
|
24
|
+
import useEntityBlockEditor from '../hooks/use-entity-block-editor';
|
|
24
25
|
|
|
25
26
|
const postTypeConfig = {
|
|
26
27
|
kind: 'postType',
|
|
@@ -137,12 +138,15 @@ describe( 'useEntityBlockEditor', () => {
|
|
|
137
138
|
title: 'block title',
|
|
138
139
|
edit,
|
|
139
140
|
} );
|
|
141
|
+
|
|
142
|
+
registerCoreBlocks();
|
|
140
143
|
} );
|
|
141
144
|
|
|
142
145
|
afterEach( () => {
|
|
143
146
|
getBlockTypes().forEach( ( block ) => {
|
|
144
147
|
unregisterBlockType( block.name );
|
|
145
148
|
} );
|
|
149
|
+
unregisterFormatType( 'core/footnote' );
|
|
146
150
|
} );
|
|
147
151
|
|
|
148
152
|
it( 'does not mutate block attributes that include an array of strings or null values', async () => {
|
package/src/test/resolvers.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
|
|
20
20
|
describe( 'getEntityRecord', () => {
|
|
21
21
|
const POST_TYPE = { slug: 'post' };
|
|
22
|
+
const POST_TYPE_RESPONSE = { json: () => Promise.resolve( POST_TYPE ) };
|
|
22
23
|
const ENTITIES = [
|
|
23
24
|
{
|
|
24
25
|
name: 'postType',
|
|
@@ -27,28 +28,37 @@ describe( 'getEntityRecord', () => {
|
|
|
27
28
|
baseURLParams: { context: 'edit' },
|
|
28
29
|
},
|
|
29
30
|
];
|
|
31
|
+
const registry = { batch: ( callback ) => callback() };
|
|
30
32
|
|
|
33
|
+
let dispatch;
|
|
31
34
|
beforeEach( async () => {
|
|
32
|
-
|
|
33
|
-
} );
|
|
34
|
-
|
|
35
|
-
it( 'yields with requested post type', async () => {
|
|
36
|
-
const dispatch = Object.assign( jest.fn(), {
|
|
35
|
+
dispatch = Object.assign( jest.fn(), {
|
|
37
36
|
receiveEntityRecords: jest.fn(),
|
|
38
37
|
__unstableAcquireStoreLock: jest.fn(),
|
|
39
38
|
__unstableReleaseStoreLock: jest.fn(),
|
|
39
|
+
receiveUserPermission: jest.fn(),
|
|
40
|
+
finishResolution: jest.fn(),
|
|
40
41
|
} );
|
|
42
|
+
triggerFetch.mockReset();
|
|
43
|
+
} );
|
|
44
|
+
|
|
45
|
+
it( 'yields with requested post type', async () => {
|
|
41
46
|
// Provide entities
|
|
42
47
|
dispatch.mockReturnValueOnce( ENTITIES );
|
|
43
48
|
|
|
44
49
|
// Provide response
|
|
45
|
-
triggerFetch.mockImplementation( () =>
|
|
50
|
+
triggerFetch.mockImplementation( () => POST_TYPE_RESPONSE );
|
|
46
51
|
|
|
47
|
-
await getEntityRecord(
|
|
52
|
+
await getEntityRecord(
|
|
53
|
+
'root',
|
|
54
|
+
'postType',
|
|
55
|
+
'post'
|
|
56
|
+
)( { dispatch, registry } );
|
|
48
57
|
|
|
49
58
|
// Fetch request should have been issued.
|
|
50
59
|
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
51
60
|
path: '/wp/v2/types/post?context=edit',
|
|
61
|
+
parse: false,
|
|
52
62
|
} );
|
|
53
63
|
|
|
54
64
|
// The record should have been received.
|
|
@@ -75,24 +85,18 @@ describe( 'getEntityRecord', () => {
|
|
|
75
85
|
const select = {
|
|
76
86
|
hasEntityRecords: jest.fn( () => {} ),
|
|
77
87
|
};
|
|
78
|
-
|
|
79
|
-
const dispatch = Object.assign( jest.fn(), {
|
|
80
|
-
receiveEntityRecords: jest.fn(),
|
|
81
|
-
__unstableAcquireStoreLock: jest.fn(),
|
|
82
|
-
__unstableReleaseStoreLock: jest.fn(),
|
|
83
|
-
} );
|
|
84
88
|
// Provide entities
|
|
85
89
|
dispatch.mockReturnValueOnce( ENTITIES );
|
|
86
90
|
|
|
87
91
|
// Provide response
|
|
88
|
-
triggerFetch.mockImplementation( () =>
|
|
92
|
+
triggerFetch.mockImplementation( () => POST_TYPE_RESPONSE );
|
|
89
93
|
|
|
90
94
|
await getEntityRecord(
|
|
91
95
|
'root',
|
|
92
96
|
'postType',
|
|
93
97
|
'post',
|
|
94
98
|
query
|
|
95
|
-
)( { dispatch, select } );
|
|
99
|
+
)( { dispatch, select, registry } );
|
|
96
100
|
|
|
97
101
|
// Check resolution cache for an existing entity that fulfills the request with query.
|
|
98
102
|
expect( select.hasEntityRecords ).toHaveBeenCalledWith(
|
|
@@ -104,6 +108,7 @@ describe( 'getEntityRecord', () => {
|
|
|
104
108
|
// Trigger apiFetch, test that the query is present in the url.
|
|
105
109
|
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
106
110
|
path: '/wp/v2/types/post?context=view&_envelope=1',
|
|
111
|
+
parse: false,
|
|
107
112
|
} );
|
|
108
113
|
|
|
109
114
|
// The record should have been received.
|
|
@@ -211,10 +216,12 @@ describe( 'getEntityRecords', () => {
|
|
|
211
216
|
} );
|
|
212
217
|
|
|
213
218
|
it( 'marks specific entity records as resolved', async () => {
|
|
219
|
+
const finishResolutions = jest.fn();
|
|
214
220
|
const dispatch = Object.assign( jest.fn(), {
|
|
215
221
|
receiveEntityRecords: jest.fn(),
|
|
216
222
|
__unstableAcquireStoreLock: jest.fn(),
|
|
217
223
|
__unstableReleaseStoreLock: jest.fn(),
|
|
224
|
+
finishResolutions,
|
|
218
225
|
} );
|
|
219
226
|
// Provide entities
|
|
220
227
|
dispatch.mockReturnValueOnce( ENTITIES );
|
|
@@ -230,16 +237,9 @@ describe( 'getEntityRecords', () => {
|
|
|
230
237
|
} );
|
|
231
238
|
|
|
232
239
|
// The record should have been received.
|
|
233
|
-
expect(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
args: [ [ ENTITIES[ 1 ].kind, ENTITIES[ 1 ].name, 2 ] ],
|
|
237
|
-
} );
|
|
238
|
-
expect( dispatch ).toHaveBeenCalledWith( {
|
|
239
|
-
type: 'FINISH_RESOLUTIONS',
|
|
240
|
-
selectorName: 'getEntityRecord',
|
|
241
|
-
args: [ [ ENTITIES[ 1 ].kind, ENTITIES[ 1 ].name, 2 ] ],
|
|
242
|
-
} );
|
|
240
|
+
expect( finishResolutions ).toHaveBeenCalledWith( 'getEntityRecord', [
|
|
241
|
+
[ ENTITIES[ 1 ].kind, ENTITIES[ 1 ].name, 2 ],
|
|
242
|
+
] );
|
|
243
243
|
} );
|
|
244
244
|
} );
|
|
245
245
|
|
|
@@ -283,26 +283,47 @@ describe( 'getEmbedPreview', () => {
|
|
|
283
283
|
} );
|
|
284
284
|
|
|
285
285
|
describe( 'canUser', () => {
|
|
286
|
-
|
|
286
|
+
const ENTITIES = [
|
|
287
|
+
{
|
|
288
|
+
name: 'media',
|
|
289
|
+
kind: 'root',
|
|
290
|
+
baseURL: '/wp/v2/media',
|
|
291
|
+
baseURLParams: { context: 'edit' },
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: 'wp_block',
|
|
295
|
+
kind: 'postType',
|
|
296
|
+
baseURL: '/wp/v2/blocks',
|
|
297
|
+
baseURLParams: { context: 'edit' },
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
let dispatch, registry;
|
|
287
302
|
beforeEach( async () => {
|
|
288
303
|
registry = {
|
|
289
304
|
select: jest.fn( () => ( {
|
|
290
305
|
hasStartedResolution: () => false,
|
|
291
306
|
} ) ),
|
|
307
|
+
batch: ( callback ) => callback(),
|
|
292
308
|
};
|
|
309
|
+
dispatch = Object.assign( jest.fn(), {
|
|
310
|
+
receiveUserPermission: jest.fn(),
|
|
311
|
+
finishResolution: jest.fn(),
|
|
312
|
+
} );
|
|
313
|
+
dispatch.mockReturnValue( ENTITIES );
|
|
293
314
|
triggerFetch.mockReset();
|
|
294
315
|
} );
|
|
295
316
|
|
|
296
317
|
it( 'does nothing when there is an API error', async () => {
|
|
297
|
-
const dispatch = Object.assign( jest.fn(), {
|
|
298
|
-
receiveUserPermission: jest.fn(),
|
|
299
|
-
} );
|
|
300
|
-
|
|
301
318
|
triggerFetch.mockImplementation( () =>
|
|
302
319
|
Promise.reject( { status: 404 } )
|
|
303
320
|
);
|
|
304
321
|
|
|
305
322
|
await canUser( 'create', 'media' )( { dispatch, registry } );
|
|
323
|
+
await canUser( 'create', { kind: 'root', name: 'media' } )( {
|
|
324
|
+
dispatch,
|
|
325
|
+
registry,
|
|
326
|
+
} );
|
|
306
327
|
|
|
307
328
|
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
308
329
|
path: '/wp/v2/media',
|
|
@@ -313,11 +334,16 @@ describe( 'canUser', () => {
|
|
|
313
334
|
expect( dispatch.receiveUserPermission ).not.toHaveBeenCalled();
|
|
314
335
|
} );
|
|
315
336
|
|
|
316
|
-
it( '
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
337
|
+
it( 'throws an error when an entity resource object is malformed', async () => {
|
|
338
|
+
await expect(
|
|
339
|
+
canUser( 'create', { name: 'wp_block' } )( {
|
|
340
|
+
dispatch,
|
|
341
|
+
registry,
|
|
342
|
+
} )
|
|
343
|
+
).rejects.toThrow( 'The entity resource object is not valid.' );
|
|
344
|
+
} );
|
|
320
345
|
|
|
346
|
+
it( 'receives false when the user is not allowed to perform an action', async () => {
|
|
321
347
|
triggerFetch.mockImplementation( () => ( {
|
|
322
348
|
headers: new Map( [ [ 'allow', 'GET' ] ] ),
|
|
323
349
|
} ) );
|
|
@@ -336,11 +362,29 @@ describe( 'canUser', () => {
|
|
|
336
362
|
);
|
|
337
363
|
} );
|
|
338
364
|
|
|
339
|
-
it( 'receives
|
|
340
|
-
|
|
341
|
-
|
|
365
|
+
it( 'receives false when the user is not allowed to perform an action on entities', async () => {
|
|
366
|
+
triggerFetch.mockImplementation( () => ( {
|
|
367
|
+
headers: new Map( [ [ 'allow', 'GET' ] ] ),
|
|
368
|
+
} ) );
|
|
369
|
+
|
|
370
|
+
await canUser( 'create', { kind: 'root', name: 'media' } )( {
|
|
371
|
+
dispatch,
|
|
372
|
+
registry,
|
|
373
|
+
} );
|
|
374
|
+
|
|
375
|
+
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
376
|
+
path: '/wp/v2/media',
|
|
377
|
+
method: 'OPTIONS',
|
|
378
|
+
parse: false,
|
|
342
379
|
} );
|
|
343
380
|
|
|
381
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
382
|
+
'create/root/media',
|
|
383
|
+
false
|
|
384
|
+
);
|
|
385
|
+
} );
|
|
386
|
+
|
|
387
|
+
it( 'receives true when the user is allowed to perform an action', async () => {
|
|
344
388
|
triggerFetch.mockImplementation( () => ( {
|
|
345
389
|
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
346
390
|
} ) );
|
|
@@ -359,11 +403,29 @@ describe( 'canUser', () => {
|
|
|
359
403
|
);
|
|
360
404
|
} );
|
|
361
405
|
|
|
362
|
-
it( 'receives true when the user is allowed to perform an action on
|
|
363
|
-
|
|
364
|
-
|
|
406
|
+
it( 'receives true when the user is allowed to perform an action on entities', async () => {
|
|
407
|
+
triggerFetch.mockImplementation( () => ( {
|
|
408
|
+
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
409
|
+
} ) );
|
|
410
|
+
|
|
411
|
+
await canUser( 'create', { kind: 'root', name: 'media' } )( {
|
|
412
|
+
dispatch,
|
|
413
|
+
registry,
|
|
414
|
+
} );
|
|
415
|
+
|
|
416
|
+
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
417
|
+
path: '/wp/v2/media',
|
|
418
|
+
method: 'OPTIONS',
|
|
419
|
+
parse: false,
|
|
365
420
|
} );
|
|
366
421
|
|
|
422
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
423
|
+
'create/root/media',
|
|
424
|
+
true
|
|
425
|
+
);
|
|
426
|
+
} );
|
|
427
|
+
|
|
428
|
+
it( 'receives true when the user is allowed to perform an action on a specific resource', async () => {
|
|
367
429
|
triggerFetch.mockImplementation( () => ( {
|
|
368
430
|
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
369
431
|
} ) );
|
|
@@ -382,12 +444,35 @@ describe( 'canUser', () => {
|
|
|
382
444
|
);
|
|
383
445
|
} );
|
|
384
446
|
|
|
385
|
-
it( '
|
|
386
|
-
|
|
387
|
-
|
|
447
|
+
it( 'receives true when the user is allowed to perform an action on a specific entity', async () => {
|
|
448
|
+
triggerFetch.mockImplementation( () => ( {
|
|
449
|
+
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
450
|
+
} ) );
|
|
451
|
+
|
|
452
|
+
await canUser( 'create', {
|
|
453
|
+
kind: 'postType',
|
|
454
|
+
name: 'wp_block',
|
|
455
|
+
id: 123,
|
|
456
|
+
} )( {
|
|
457
|
+
dispatch,
|
|
458
|
+
registry,
|
|
459
|
+
} );
|
|
460
|
+
|
|
461
|
+
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
462
|
+
path: '/wp/v2/blocks/123',
|
|
463
|
+
method: 'OPTIONS',
|
|
464
|
+
parse: false,
|
|
388
465
|
} );
|
|
389
466
|
|
|
467
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
468
|
+
'create/postType/wp_block/123',
|
|
469
|
+
true
|
|
470
|
+
);
|
|
471
|
+
} );
|
|
472
|
+
|
|
473
|
+
it( 'runs apiFetch only once per resource', async () => {
|
|
390
474
|
registry = {
|
|
475
|
+
...registry,
|
|
391
476
|
select: () => ( {
|
|
392
477
|
hasStartedResolution: ( _, [ action ] ) => action === 'read',
|
|
393
478
|
} ),
|
|
@@ -412,12 +497,48 @@ describe( 'canUser', () => {
|
|
|
412
497
|
);
|
|
413
498
|
} );
|
|
414
499
|
|
|
415
|
-
it( '
|
|
416
|
-
|
|
417
|
-
|
|
500
|
+
it( 'runs apiFetch only once per entity', async () => {
|
|
501
|
+
registry = {
|
|
502
|
+
...registry,
|
|
503
|
+
select: () => ( {
|
|
504
|
+
hasStartedResolution: ( _, [ action ] ) => action === 'read',
|
|
505
|
+
} ),
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
triggerFetch.mockImplementation( () => ( {
|
|
509
|
+
headers: new Map( [ [ 'allow', 'POST, GET' ] ] ),
|
|
510
|
+
} ) );
|
|
511
|
+
|
|
512
|
+
await canUser( 'create', {
|
|
513
|
+
kind: 'postType',
|
|
514
|
+
name: 'wp_block',
|
|
515
|
+
} )( {
|
|
516
|
+
dispatch,
|
|
517
|
+
registry,
|
|
518
|
+
} );
|
|
519
|
+
await canUser( 'read', {
|
|
520
|
+
kind: 'postType',
|
|
521
|
+
name: 'wp_block',
|
|
522
|
+
} )( {
|
|
523
|
+
dispatch,
|
|
524
|
+
registry,
|
|
418
525
|
} );
|
|
419
526
|
|
|
527
|
+
expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
|
|
528
|
+
|
|
529
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
530
|
+
'create/postType/wp_block',
|
|
531
|
+
true
|
|
532
|
+
);
|
|
533
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
534
|
+
'read/postType/wp_block',
|
|
535
|
+
true
|
|
536
|
+
);
|
|
537
|
+
} );
|
|
538
|
+
|
|
539
|
+
it( 'retrieves all permissions even when ID is not given', async () => {
|
|
420
540
|
registry = {
|
|
541
|
+
...registry,
|
|
421
542
|
select: () => ( {
|
|
422
543
|
hasStartedResolution: ( _, [ action ] ) => action === 'read',
|
|
423
544
|
} ),
|
|
@@ -451,11 +572,8 @@ describe( 'canUser', () => {
|
|
|
451
572
|
} );
|
|
452
573
|
|
|
453
574
|
it( 'runs apiFetch only once per resource ID', async () => {
|
|
454
|
-
const dispatch = Object.assign( jest.fn(), {
|
|
455
|
-
receiveUserPermission: jest.fn(),
|
|
456
|
-
} );
|
|
457
|
-
|
|
458
575
|
registry = {
|
|
576
|
+
...registry,
|
|
459
577
|
select: () => ( {
|
|
460
578
|
hasStartedResolution: ( _, [ action ] ) => action === 'create',
|
|
461
579
|
} ),
|
|
@@ -489,6 +607,59 @@ describe( 'canUser', () => {
|
|
|
489
607
|
true
|
|
490
608
|
);
|
|
491
609
|
} );
|
|
610
|
+
|
|
611
|
+
it( 'runs apiFetch only once per entity ID', async () => {
|
|
612
|
+
registry = {
|
|
613
|
+
...registry,
|
|
614
|
+
select: () => ( {
|
|
615
|
+
hasStartedResolution: ( _, [ action ] ) => action === 'create',
|
|
616
|
+
} ),
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
triggerFetch.mockImplementation( () => ( {
|
|
620
|
+
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
621
|
+
} ) );
|
|
622
|
+
|
|
623
|
+
await canUser( 'create', {
|
|
624
|
+
kind: 'postType',
|
|
625
|
+
name: 'wp_block',
|
|
626
|
+
id: 123,
|
|
627
|
+
} )( { dispatch, registry } );
|
|
628
|
+
await canUser( 'read', {
|
|
629
|
+
kind: 'postType',
|
|
630
|
+
name: 'wp_block',
|
|
631
|
+
id: 123,
|
|
632
|
+
} )( { dispatch, registry } );
|
|
633
|
+
await canUser( 'update', {
|
|
634
|
+
kind: 'postType',
|
|
635
|
+
name: 'wp_block',
|
|
636
|
+
id: 123,
|
|
637
|
+
} )( { dispatch, registry } );
|
|
638
|
+
await canUser( 'delete', {
|
|
639
|
+
kind: 'postType',
|
|
640
|
+
name: 'wp_block',
|
|
641
|
+
id: 123,
|
|
642
|
+
} )( { dispatch, registry } );
|
|
643
|
+
|
|
644
|
+
expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
|
|
645
|
+
|
|
646
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
647
|
+
'create/postType/wp_block/123',
|
|
648
|
+
true
|
|
649
|
+
);
|
|
650
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
651
|
+
'read/postType/wp_block/123',
|
|
652
|
+
true
|
|
653
|
+
);
|
|
654
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
655
|
+
'update/postType/wp_block/123',
|
|
656
|
+
true
|
|
657
|
+
);
|
|
658
|
+
expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
|
|
659
|
+
'delete/postType/wp_block/123',
|
|
660
|
+
true
|
|
661
|
+
);
|
|
662
|
+
} );
|
|
492
663
|
} );
|
|
493
664
|
|
|
494
665
|
describe( 'getAutosaves', () => {
|
package/src/test/selectors.js
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
getEmbedPreview,
|
|
19
19
|
isPreviewEmbedFallback,
|
|
20
20
|
canUser,
|
|
21
|
-
canUserEditEntityRecord,
|
|
22
21
|
getAutosave,
|
|
23
22
|
getAutosaves,
|
|
24
23
|
getCurrentUser,
|
|
@@ -690,79 +689,43 @@ describe( 'canUser', () => {
|
|
|
690
689
|
userPermissions: {},
|
|
691
690
|
} );
|
|
692
691
|
expect( canUser( state, 'create', 'media' ) ).toBe( undefined );
|
|
692
|
+
expect(
|
|
693
|
+
canUser( state, 'create', { kind: 'root', name: 'media' } )
|
|
694
|
+
).toBe( undefined );
|
|
695
|
+
} );
|
|
696
|
+
|
|
697
|
+
it( 'returns null when entity kind or name is missing', () => {
|
|
698
|
+
const state = deepFreeze( {
|
|
699
|
+
userPermissions: {},
|
|
700
|
+
} );
|
|
701
|
+
expect( canUser( state, 'create', { name: 'media' } ) ).toBe( false );
|
|
702
|
+
expect( canUser( state, 'create', { kind: 'root' } ) ).toBe( false );
|
|
693
703
|
} );
|
|
694
704
|
|
|
695
705
|
it( 'returns whether an action can be performed', () => {
|
|
696
706
|
const state = deepFreeze( {
|
|
697
707
|
userPermissions: {
|
|
698
708
|
'create/media': false,
|
|
709
|
+
'create/root/media': false,
|
|
699
710
|
},
|
|
700
711
|
} );
|
|
701
712
|
expect( canUser( state, 'create', 'media' ) ).toBe( false );
|
|
713
|
+
expect(
|
|
714
|
+
canUser( state, 'create', { kind: 'root', name: 'media' } )
|
|
715
|
+
).toBe( false );
|
|
702
716
|
} );
|
|
703
717
|
|
|
704
718
|
it( 'returns whether an action can be performed for a given resource', () => {
|
|
705
719
|
const state = deepFreeze( {
|
|
706
720
|
userPermissions: {
|
|
707
721
|
'create/media/123': false,
|
|
722
|
+
'create/root/media/123': false,
|
|
708
723
|
},
|
|
709
724
|
} );
|
|
710
725
|
expect( canUser( state, 'create', 'media', 123 ) ).toBe( false );
|
|
711
|
-
} );
|
|
712
|
-
} );
|
|
713
|
-
|
|
714
|
-
describe( 'canUserEditEntityRecord', () => {
|
|
715
|
-
it( 'returns false by default', () => {
|
|
716
|
-
const state = deepFreeze( {
|
|
717
|
-
userPermissions: {},
|
|
718
|
-
entities: { records: {} },
|
|
719
|
-
} );
|
|
720
|
-
expect( canUserEditEntityRecord( state, 'postType', 'post' ) ).toBe(
|
|
721
|
-
false
|
|
722
|
-
);
|
|
723
|
-
} );
|
|
724
|
-
|
|
725
|
-
it( 'returns whether the user can edit', () => {
|
|
726
|
-
const state = deepFreeze( {
|
|
727
|
-
userPermissions: {
|
|
728
|
-
'create/posts': false,
|
|
729
|
-
'update/posts/1': true,
|
|
730
|
-
},
|
|
731
|
-
entities: {
|
|
732
|
-
config: [
|
|
733
|
-
{
|
|
734
|
-
kind: 'postType',
|
|
735
|
-
name: 'post',
|
|
736
|
-
__unstable_rest_base: 'posts',
|
|
737
|
-
},
|
|
738
|
-
],
|
|
739
|
-
records: {
|
|
740
|
-
root: {
|
|
741
|
-
postType: {
|
|
742
|
-
queriedData: {
|
|
743
|
-
items: {
|
|
744
|
-
default: {
|
|
745
|
-
post: {
|
|
746
|
-
slug: 'post',
|
|
747
|
-
__unstable: 'posts',
|
|
748
|
-
},
|
|
749
|
-
},
|
|
750
|
-
},
|
|
751
|
-
itemIsComplete: {
|
|
752
|
-
default: {
|
|
753
|
-
post: true,
|
|
754
|
-
},
|
|
755
|
-
},
|
|
756
|
-
queries: {},
|
|
757
|
-
},
|
|
758
|
-
},
|
|
759
|
-
},
|
|
760
|
-
},
|
|
761
|
-
},
|
|
762
|
-
} );
|
|
763
726
|
expect(
|
|
764
|
-
|
|
765
|
-
).toBe(
|
|
727
|
+
canUser( state, 'create', { kind: 'root', name: 'media', id: 123 } )
|
|
728
|
+
).toBe( false );
|
|
766
729
|
} );
|
|
767
730
|
} );
|
|
768
731
|
|
package/src/utils/index.js
CHANGED
|
@@ -9,3 +9,8 @@ export { default as isRawAttribute } from './is-raw-attribute';
|
|
|
9
9
|
export { default as setNestedValue } from './set-nested-value';
|
|
10
10
|
export { default as getNestedValue } from './get-nested-value';
|
|
11
11
|
export { default as isNumericID } from './is-numeric-id';
|
|
12
|
+
export {
|
|
13
|
+
getUserPermissionCacheKey,
|
|
14
|
+
getUserPermissionsFromResponse,
|
|
15
|
+
ALLOWED_RESOURCE_ACTIONS,
|
|
16
|
+
} from './user-permissions';
|