@wordpress/core-data 7.6.0 → 7.7.1-next.5368f64a9.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +2 -2
  3. package/build/actions.js +39 -16
  4. package/build/actions.js.map +1 -1
  5. package/build/footnotes/index.js +5 -1
  6. package/build/footnotes/index.js.map +1 -1
  7. package/build/hooks/use-entity-prop.js +4 -4
  8. package/build/hooks/use-entity-prop.js.map +1 -1
  9. package/build/reducer.js +5 -0
  10. package/build/reducer.js.map +1 -1
  11. package/build/resolvers.js +77 -36
  12. package/build/resolvers.js.map +1 -1
  13. package/build/utils/index.js +2 -2
  14. package/build/utils/index.js.map +1 -1
  15. package/build/utils/user-permissions.js +5 -7
  16. package/build/utils/user-permissions.js.map +1 -1
  17. package/build-module/actions.js +38 -16
  18. package/build-module/actions.js.map +1 -1
  19. package/build-module/footnotes/index.js +5 -1
  20. package/build-module/footnotes/index.js.map +1 -1
  21. package/build-module/hooks/use-entity-prop.js +4 -4
  22. package/build-module/hooks/use-entity-prop.js.map +1 -1
  23. package/build-module/reducer.js +5 -0
  24. package/build-module/reducer.js.map +1 -1
  25. package/build-module/resolvers.js +78 -37
  26. package/build-module/resolvers.js.map +1 -1
  27. package/build-module/utils/index.js +1 -1
  28. package/build-module/utils/index.js.map +1 -1
  29. package/build-module/utils/user-permissions.js +4 -6
  30. package/build-module/utils/user-permissions.js.map +1 -1
  31. package/build-types/actions.d.ts +20 -2
  32. package/build-types/actions.d.ts.map +1 -1
  33. package/build-types/footnotes/index.d.ts.map +1 -1
  34. package/build-types/hooks/use-entity-prop.d.ts +5 -5
  35. package/build-types/hooks/use-entity-prop.d.ts.map +1 -1
  36. package/build-types/index.d.ts +5 -2
  37. package/build-types/index.d.ts.map +1 -1
  38. package/build-types/reducer.d.ts.map +1 -1
  39. package/build-types/resolvers.d.ts +5 -3
  40. package/build-types/resolvers.d.ts.map +1 -1
  41. package/build-types/utils/index.d.ts +1 -1
  42. package/build-types/utils/user-permissions.d.ts +1 -1
  43. package/build-types/utils/user-permissions.d.ts.map +1 -1
  44. package/package.json +18 -18
  45. package/src/actions.js +38 -16
  46. package/src/footnotes/index.js +9 -4
  47. package/src/hooks/test/use-entity-records.js +2 -2
  48. package/src/hooks/use-entity-prop.js +4 -4
  49. package/src/reducer.js +5 -0
  50. package/src/resolvers.js +117 -54
  51. package/src/test/resolvers.js +5 -12
  52. package/src/utils/index.js +1 -1
  53. package/src/utils/user-permissions.js +4 -6
  54. package/tsconfig.tsbuildinfo +1 -1
package/src/resolvers.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  forwardResolver,
20
20
  getNormalizedCommaSeparable,
21
21
  getUserPermissionCacheKey,
22
- getUserPermissionsFromResponse,
22
+ getUserPermissionsFromAllowHeader,
23
23
  ALLOWED_RESOURCE_ACTIONS,
24
24
  } from './utils';
25
25
  import { getSyncProvider } from './sync';
@@ -155,7 +155,7 @@ export const getEntityRecord =
155
155
  }
156
156
  );
157
157
 
158
- if ( query !== undefined ) {
158
+ if ( query !== undefined && query._fields ) {
159
159
  query = { ...query, include: [ key ] };
160
160
 
161
161
  // The resolution cache won't consider query as reusable based on the
@@ -173,26 +173,36 @@ export const getEntityRecord =
173
173
 
174
174
  const response = await apiFetch( { path, parse: false } );
175
175
  const record = await response.json();
176
- const permissions = getUserPermissionsFromResponse( response );
176
+ const permissions = getUserPermissionsFromAllowHeader(
177
+ response.headers?.get( 'allow' )
178
+ );
179
+
180
+ const canUserResolutionsArgs = [];
181
+ const receiveUserPermissionArgs = {};
182
+ for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
183
+ receiveUserPermissionArgs[
184
+ getUserPermissionCacheKey( action, {
185
+ kind,
186
+ name,
187
+ id: key,
188
+ } )
189
+ ] = permissions[ action ];
190
+
191
+ canUserResolutionsArgs.push( [
192
+ action,
193
+ { kind, name, id: key },
194
+ ] );
195
+ }
177
196
 
178
197
  registry.batch( () => {
179
198
  dispatch.receiveEntityRecords( kind, name, record, query );
180
-
181
- for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
182
- const permissionKey = getUserPermissionCacheKey(
183
- action,
184
- { kind, name, id: key }
185
- );
186
-
187
- dispatch.receiveUserPermission(
188
- permissionKey,
189
- permissions[ action ]
190
- );
191
- dispatch.finishResolution( 'canUser', [
192
- action,
193
- { kind, name, id: key },
194
- ] );
195
- }
199
+ dispatch.receiveUserPermissions(
200
+ receiveUserPermissionArgs
201
+ );
202
+ dispatch.finishResolutions(
203
+ 'canUser',
204
+ canUserResolutionsArgs
205
+ );
196
206
  } );
197
207
  }
198
208
  } finally {
@@ -271,6 +281,10 @@ export const getEntityRecords =
271
281
  };
272
282
  } else {
273
283
  records = Object.values( await apiFetch( { path } ) );
284
+ meta = {
285
+ totalItems: records.length,
286
+ totalPages: 1,
287
+ };
274
288
  }
275
289
 
276
290
  // If we request fields but the result doesn't contain the fields,
@@ -299,19 +313,55 @@ export const getEntityRecords =
299
313
  meta
300
314
  );
301
315
 
302
- // When requesting all fields, the list of results can be used to
303
- // resolve the `getEntityRecord` selector in addition to `getEntityRecords`.
316
+ // When requesting all fields, the list of results can be used to resolve
317
+ // the `getEntityRecord` and `canUser` selectors in addition to `getEntityRecords`.
304
318
  // See https://github.com/WordPress/gutenberg/pull/26575
319
+ // See https://github.com/WordPress/gutenberg/pull/64504
305
320
  if ( ! query?._fields && ! query.context ) {
306
321
  const key = entityConfig.key || DEFAULT_ENTITY_KEY;
307
322
  const resolutionsArgs = records
308
323
  .filter( ( record ) => record?.[ key ] )
309
324
  .map( ( record ) => [ kind, name, record[ key ] ] );
310
325
 
326
+ const targetHints = records
327
+ .filter( ( record ) => record?.[ key ] )
328
+ .map( ( record ) => ( {
329
+ id: record[ key ],
330
+ permissions: getUserPermissionsFromAllowHeader(
331
+ record?._links?.self?.[ 0 ].targetHints.allow
332
+ ),
333
+ } ) );
334
+
335
+ const canUserResolutionsArgs = [];
336
+ const receiveUserPermissionArgs = {};
337
+ for ( const targetHint of targetHints ) {
338
+ for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
339
+ canUserResolutionsArgs.push( [
340
+ action,
341
+ { kind, name, id: targetHint.id },
342
+ ] );
343
+
344
+ receiveUserPermissionArgs[
345
+ getUserPermissionCacheKey( action, {
346
+ kind,
347
+ name,
348
+ id: targetHint.id,
349
+ } )
350
+ ] = targetHint.permissions[ action ];
351
+ }
352
+ }
353
+
354
+ dispatch.receiveUserPermissions(
355
+ receiveUserPermissionArgs
356
+ );
311
357
  dispatch.finishResolutions(
312
358
  'getEntityRecord',
313
359
  resolutionsArgs
314
360
  );
361
+ dispatch.finishResolutions(
362
+ 'canUser',
363
+ canUserResolutionsArgs
364
+ );
315
365
  }
316
366
 
317
367
  dispatch.__unstableReleaseStoreLock( lock );
@@ -440,7 +490,12 @@ export const canUser =
440
490
  return;
441
491
  }
442
492
 
443
- const permissions = getUserPermissionsFromResponse( response );
493
+ // Optional chaining operator is used here because the API requests don't
494
+ // return the expected result in the React native version. Instead, API requests
495
+ // only return the result, without including response properties like the headers.
496
+ const permissions = getUserPermissionsFromAllowHeader(
497
+ response.headers?.get( 'allow' )
498
+ );
444
499
  registry.batch( () => {
445
500
  for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
446
501
  const key = getUserPermissionCacheKey( action, resource, id );
@@ -463,9 +518,9 @@ export const canUser =
463
518
  * Checks whether the current user can perform the given action on the given
464
519
  * REST resource.
465
520
  *
466
- * @param {string} kind Entity kind.
467
- * @param {string} name Entity name.
468
- * @param {string} recordId Record's id.
521
+ * @param {string} kind Entity kind.
522
+ * @param {string} name Entity name.
523
+ * @param {number|string} recordId Record's id.
469
524
  */
470
525
  export const canUserEditEntityRecord =
471
526
  ( kind, name, recordId ) =>
@@ -700,7 +755,7 @@ export const getUserPatternCategories =
700
755
 
701
756
  export const getNavigationFallbackId =
702
757
  () =>
703
- async ( { dispatch, select } ) => {
758
+ async ( { dispatch, select, registry } ) => {
704
759
  const fallback = await apiFetch( {
705
760
  path: addQueryArgs( '/wp-block-editor/v1/navigation-fallback', {
706
761
  _embed: true,
@@ -709,9 +764,13 @@ export const getNavigationFallbackId =
709
764
 
710
765
  const record = fallback?._embedded?.self;
711
766
 
712
- dispatch.receiveNavigationFallbackId( fallback?.id );
767
+ registry.batch( () => {
768
+ dispatch.receiveNavigationFallbackId( fallback?.id );
769
+
770
+ if ( ! record ) {
771
+ return;
772
+ }
713
773
 
714
- if ( record ) {
715
774
  // If the fallback is already in the store, don't invalidate navigation queries.
716
775
  // Otherwise, invalidate the cache for the scenario where there were no Navigation
717
776
  // posts in the state and the fallback created one.
@@ -735,7 +794,7 @@ export const getNavigationFallbackId =
735
794
  'wp_navigation',
736
795
  fallback.id,
737
796
  ] );
738
- }
797
+ } );
739
798
  };
740
799
 
741
800
  export const getDefaultTemplateId =
@@ -762,7 +821,7 @@ export const getDefaultTemplateId =
762
821
  */
763
822
  export const getRevisions =
764
823
  ( kind, name, recordKey, query = {} ) =>
765
- async ( { dispatch } ) => {
824
+ async ( { dispatch, registry } ) => {
766
825
  const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
767
826
  const entityConfig = configs.find(
768
827
  ( config ) => config.name === name && config.kind === kind
@@ -829,32 +888,36 @@ export const getRevisions =
829
888
  } );
830
889
  }
831
890
 
832
- dispatch.receiveRevisions(
833
- kind,
834
- name,
835
- recordKey,
836
- records,
837
- query,
838
- false,
839
- meta
840
- );
891
+ registry.batch( () => {
892
+ dispatch.receiveRevisions(
893
+ kind,
894
+ name,
895
+ recordKey,
896
+ records,
897
+ query,
898
+ false,
899
+ meta
900
+ );
841
901
 
842
- // When requesting all fields, the list of results can be used to
843
- // resolve the `getRevision` selector in addition to `getRevisions`.
844
- if ( ! query?._fields && ! query.context ) {
845
- const key = entityConfig.key || DEFAULT_ENTITY_KEY;
846
- const resolutionsArgs = records
847
- .filter( ( record ) => record[ key ] )
848
- .map( ( record ) => [
849
- kind,
850
- name,
851
- recordKey,
852
- record[ key ],
853
- ] );
902
+ // When requesting all fields, the list of results can be used to
903
+ // resolve the `getRevision` selector in addition to `getRevisions`.
904
+ if ( ! query?._fields && ! query.context ) {
905
+ const key = entityConfig.key || DEFAULT_ENTITY_KEY;
906
+ const resolutionsArgs = records
907
+ .filter( ( record ) => record[ key ] )
908
+ .map( ( record ) => [
909
+ kind,
910
+ name,
911
+ recordKey,
912
+ record[ key ],
913
+ ] );
854
914
 
855
- dispatch.startResolutions( 'getRevision', resolutionsArgs );
856
- dispatch.finishResolutions( 'getRevision', resolutionsArgs );
857
- }
915
+ dispatch.finishResolutions(
916
+ 'getRevision',
917
+ resolutionsArgs
918
+ );
919
+ }
920
+ } );
858
921
  }
859
922
  };
860
923
 
@@ -36,8 +36,8 @@ describe( 'getEntityRecord', () => {
36
36
  receiveEntityRecords: jest.fn(),
37
37
  __unstableAcquireStoreLock: jest.fn(),
38
38
  __unstableReleaseStoreLock: jest.fn(),
39
- receiveUserPermission: jest.fn(),
40
- finishResolution: jest.fn(),
39
+ receiveUserPermissions: jest.fn(),
40
+ finishResolutions: jest.fn(),
41
41
  } );
42
42
  triggerFetch.mockReset();
43
43
  } );
@@ -80,7 +80,6 @@ describe( 'getEntityRecord', () => {
80
80
 
81
81
  it( 'accepts a query that overrides default api path', async () => {
82
82
  const query = { context: 'view', _envelope: '1' };
83
- const queryObj = { include: [ 'post' ], ...query };
84
83
 
85
84
  const select = {
86
85
  hasEntityRecords: jest.fn( () => {} ),
@@ -98,13 +97,6 @@ describe( 'getEntityRecord', () => {
98
97
  query
99
98
  )( { dispatch, select, registry } );
100
99
 
101
- // Check resolution cache for an existing entity that fulfills the request with query.
102
- expect( select.hasEntityRecords ).toHaveBeenCalledWith(
103
- 'root',
104
- 'postType',
105
- queryObj
106
- );
107
-
108
100
  // Trigger apiFetch, test that the query is present in the url.
109
101
  expect( triggerFetch ).toHaveBeenCalledWith( {
110
102
  path: '/wp/v2/types/post?context=view&_envelope=1',
@@ -116,7 +108,7 @@ describe( 'getEntityRecord', () => {
116
108
  'root',
117
109
  'postType',
118
110
  POST_TYPE,
119
- queryObj
111
+ query
120
112
  );
121
113
 
122
114
  // Locks should have been acquired and released.
@@ -181,7 +173,7 @@ describe( 'getEntityRecords', () => {
181
173
  {},
182
174
  false,
183
175
  undefined,
184
- undefined
176
+ { totalItems: 2, totalPages: 1 }
185
177
  );
186
178
  } );
187
179
 
@@ -219,6 +211,7 @@ describe( 'getEntityRecords', () => {
219
211
  const finishResolutions = jest.fn();
220
212
  const dispatch = Object.assign( jest.fn(), {
221
213
  receiveEntityRecords: jest.fn(),
214
+ receiveUserPermissions: jest.fn(),
222
215
  __unstableAcquireStoreLock: jest.fn(),
223
216
  __unstableReleaseStoreLock: jest.fn(),
224
217
  finishResolutions,
@@ -11,6 +11,6 @@ export { default as getNestedValue } from './get-nested-value';
11
11
  export { default as isNumericID } from './is-numeric-id';
12
12
  export {
13
13
  getUserPermissionCacheKey,
14
- getUserPermissionsFromResponse,
14
+ getUserPermissionsFromAllowHeader,
15
15
  ALLOWED_RESOURCE_ACTIONS,
16
16
  } from './user-permissions';
@@ -5,13 +5,11 @@ export const ALLOWED_RESOURCE_ACTIONS = [
5
5
  'delete',
6
6
  ];
7
7
 
8
- export function getUserPermissionsFromResponse( response ) {
8
+ export function getUserPermissionsFromAllowHeader( allowedMethods ) {
9
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' ) || '';
10
+ if ( ! allowedMethods ) {
11
+ return permissions;
12
+ }
15
13
 
16
14
  const methods = {
17
15
  create: 'POST',