@wordpress/core-data 7.3.0 → 7.5.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 +1 -1
- 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/footnotes/get-rich-text-values-cached.js +2 -2
- package/build/footnotes/get-rich-text-values-cached.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-entity-records.js +39 -0
- package/build/hooks/use-entity-records.js.map +1 -1
- package/build/hooks/use-resource-permissions.js +25 -8
- package/build/hooks/use-resource-permissions.js.map +1 -1
- package/build/index.js +14 -2
- package/build/index.js.map +1 -1
- package/build/lock-unlock.js +18 -0
- package/build/lock-unlock.js.map +1 -0
- package/build/private-apis.js +8 -9
- package/build/private-apis.js.map +1 -1
- package/build/private-selectors.js +35 -0
- package/build/private-selectors.js.map +1 -1
- package/build/resolvers.js +64 -60
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +16 -8
- 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 +1 -1
- 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 -183
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/footnotes/get-rich-text-values-cached.js +1 -1
- package/build-module/footnotes/get-rich-text-values-cached.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-entity-records.js +38 -0
- package/build-module/hooks/use-entity-records.js.map +1 -1
- package/build-module/hooks/use-resource-permissions.js +25 -8
- package/build-module/hooks/use-resource-permissions.js.map +1 -1
- package/build-module/index.js +2 -1
- package/build-module/index.js.map +1 -1
- package/build-module/lock-unlock.js +9 -0
- package/build-module/lock-unlock.js.map +1 -0
- package/build-module/private-apis.js +7 -6
- package/build-module/private-apis.js.map +1 -1
- package/build-module/private-selectors.js +33 -0
- package/build-module/private-selectors.js.map +1 -1
- package/build-module/resolvers.js +65 -61
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +17 -9
- 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 -47
- package/build-types/entity-provider.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-entity-records.d.ts +1 -0
- package/build-types/hooks/use-entity-records.d.ts.map +1 -1
- 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 +36 -32
- package/build-types/index.d.ts.map +1 -1
- package/build-types/lock-unlock.d.ts +3 -0
- package/build-types/lock-unlock.d.ts.map +1 -0
- package/build-types/locks/reducer.d.ts +1 -1
- package/build-types/locks/reducer.d.ts.map +1 -1
- package/build-types/private-apis.d.ts +1 -2
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +15 -0
- package/build-types/private-selectors.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 +1 -1
- package/src/entity-context.js +6 -0
- package/src/entity-provider.js +2 -209
- package/src/footnotes/get-rich-text-values-cached.js +1 -1
- 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-entity-records.ts +50 -0
- package/src/hooks/use-resource-permissions.ts +46 -9
- package/src/index.js +2 -1
- package/src/lock-unlock.js +10 -0
- package/src/private-apis.js +7 -7
- package/src/private-selectors.ts +43 -0
- package/src/resolvers.js +85 -67
- package/src/selectors.ts +18 -9
- package/src/test/entity-provider.js +6 -2
- package/src/test/resolvers.js +217 -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 -1
- package/tsconfig.tsbuildinfo +1 -1
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,7 +283,22 @@ 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( () => ( {
|
|
@@ -291,19 +306,24 @@ describe( 'canUser', () => {
|
|
|
291
306
|
} ) ),
|
|
292
307
|
batch: ( callback ) => callback(),
|
|
293
308
|
};
|
|
309
|
+
dispatch = Object.assign( jest.fn(), {
|
|
310
|
+
receiveUserPermission: jest.fn(),
|
|
311
|
+
finishResolution: jest.fn(),
|
|
312
|
+
} );
|
|
313
|
+
dispatch.mockReturnValue( ENTITIES );
|
|
294
314
|
triggerFetch.mockReset();
|
|
295
315
|
} );
|
|
296
316
|
|
|
297
317
|
it( 'does nothing when there is an API error', async () => {
|
|
298
|
-
const dispatch = Object.assign( jest.fn(), {
|
|
299
|
-
receiveUserPermission: jest.fn(),
|
|
300
|
-
} );
|
|
301
|
-
|
|
302
318
|
triggerFetch.mockImplementation( () =>
|
|
303
319
|
Promise.reject( { status: 404 } )
|
|
304
320
|
);
|
|
305
321
|
|
|
306
322
|
await canUser( 'create', 'media' )( { dispatch, registry } );
|
|
323
|
+
await canUser( 'create', { kind: 'root', name: 'media' } )( {
|
|
324
|
+
dispatch,
|
|
325
|
+
registry,
|
|
326
|
+
} );
|
|
307
327
|
|
|
308
328
|
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
309
329
|
path: '/wp/v2/media',
|
|
@@ -314,11 +334,16 @@ describe( 'canUser', () => {
|
|
|
314
334
|
expect( dispatch.receiveUserPermission ).not.toHaveBeenCalled();
|
|
315
335
|
} );
|
|
316
336
|
|
|
317
|
-
it( '
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
+
} );
|
|
321
345
|
|
|
346
|
+
it( 'receives false when the user is not allowed to perform an action', async () => {
|
|
322
347
|
triggerFetch.mockImplementation( () => ( {
|
|
323
348
|
headers: new Map( [ [ 'allow', 'GET' ] ] ),
|
|
324
349
|
} ) );
|
|
@@ -337,11 +362,29 @@ describe( 'canUser', () => {
|
|
|
337
362
|
);
|
|
338
363
|
} );
|
|
339
364
|
|
|
340
|
-
it( 'receives
|
|
341
|
-
|
|
342
|
-
|
|
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,
|
|
343
379
|
} );
|
|
344
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 () => {
|
|
345
388
|
triggerFetch.mockImplementation( () => ( {
|
|
346
389
|
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
347
390
|
} ) );
|
|
@@ -360,11 +403,29 @@ describe( 'canUser', () => {
|
|
|
360
403
|
);
|
|
361
404
|
} );
|
|
362
405
|
|
|
363
|
-
it( 'receives true when the user is allowed to perform an action on
|
|
364
|
-
|
|
365
|
-
|
|
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,
|
|
366
420
|
} );
|
|
367
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 () => {
|
|
368
429
|
triggerFetch.mockImplementation( () => ( {
|
|
369
430
|
headers: new Map( [ [ 'allow', 'POST, GET, PUT, DELETE' ] ] ),
|
|
370
431
|
} ) );
|
|
@@ -383,11 +444,33 @@ describe( 'canUser', () => {
|
|
|
383
444
|
);
|
|
384
445
|
} );
|
|
385
446
|
|
|
386
|
-
it( '
|
|
387
|
-
|
|
388
|
-
|
|
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,
|
|
389
459
|
} );
|
|
390
460
|
|
|
461
|
+
expect( triggerFetch ).toHaveBeenCalledWith( {
|
|
462
|
+
path: '/wp/v2/blocks/123',
|
|
463
|
+
method: 'OPTIONS',
|
|
464
|
+
parse: false,
|
|
465
|
+
} );
|
|
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 () => {
|
|
391
474
|
registry = {
|
|
392
475
|
...registry,
|
|
393
476
|
select: () => ( {
|
|
@@ -414,11 +497,46 @@ describe( 'canUser', () => {
|
|
|
414
497
|
);
|
|
415
498
|
} );
|
|
416
499
|
|
|
417
|
-
it( '
|
|
418
|
-
|
|
419
|
-
|
|
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,
|
|
420
525
|
} );
|
|
421
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 () => {
|
|
422
540
|
registry = {
|
|
423
541
|
...registry,
|
|
424
542
|
select: () => ( {
|
|
@@ -454,10 +572,6 @@ describe( 'canUser', () => {
|
|
|
454
572
|
} );
|
|
455
573
|
|
|
456
574
|
it( 'runs apiFetch only once per resource ID', async () => {
|
|
457
|
-
const dispatch = Object.assign( jest.fn(), {
|
|
458
|
-
receiveUserPermission: jest.fn(),
|
|
459
|
-
} );
|
|
460
|
-
|
|
461
575
|
registry = {
|
|
462
576
|
...registry,
|
|
463
577
|
select: () => ( {
|
|
@@ -493,6 +607,59 @@ describe( 'canUser', () => {
|
|
|
493
607
|
true
|
|
494
608
|
);
|
|
495
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
|
+
} );
|
|
496
663
|
} );
|
|
497
664
|
|
|
498
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';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const ALLOWED_RESOURCE_ACTIONS = [
|
|
2
|
+
'create',
|
|
3
|
+
'read',
|
|
4
|
+
'update',
|
|
5
|
+
'delete',
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function getUserPermissionsFromResponse( response ) {
|
|
9
|
+
const permissions = {};
|
|
10
|
+
|
|
11
|
+
// Optional chaining operator is used here because the API requests don't
|
|
12
|
+
// return the expected result in the React native version. Instead, API requests
|
|
13
|
+
// only return the result, without including response properties like the headers.
|
|
14
|
+
const allowedMethods = response.headers?.get( 'allow' ) || '';
|
|
15
|
+
|
|
16
|
+
const methods = {
|
|
17
|
+
create: 'POST',
|
|
18
|
+
read: 'GET',
|
|
19
|
+
update: 'PUT',
|
|
20
|
+
delete: 'DELETE',
|
|
21
|
+
};
|
|
22
|
+
for ( const [ actionName, methodName ] of Object.entries( methods ) ) {
|
|
23
|
+
permissions[ actionName ] = allowedMethods.includes( methodName );
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return permissions;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getUserPermissionCacheKey( action, resource, id ) {
|
|
30
|
+
const key = (
|
|
31
|
+
typeof resource === 'object'
|
|
32
|
+
? [ action, resource.kind, resource.name, resource.id ]
|
|
33
|
+
: [ action, resource, id ]
|
|
34
|
+
)
|
|
35
|
+
.filter( Boolean )
|
|
36
|
+
.join( '/' );
|
|
37
|
+
|
|
38
|
+
return key;
|
|
39
|
+
}
|