@wordpress/core-data 4.9.0 → 4.12.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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -81
  3. package/build/actions.js +27 -0
  4. package/build/actions.js.map +1 -1
  5. package/build/batch/default-processor.js +13 -7
  6. package/build/batch/default-processor.js.map +1 -1
  7. package/build/hooks/use-resource-permissions.js +94 -0
  8. package/build/hooks/use-resource-permissions.js.map +1 -0
  9. package/build/index.js +28 -1
  10. package/build/index.js.map +1 -1
  11. package/build/reducer.js +4 -1
  12. package/build/reducer.js.map +1 -1
  13. package/build/resolvers.js +9 -17
  14. package/build/resolvers.js.map +1 -1
  15. package/build/selectors.js +5 -3
  16. package/build/selectors.js.map +1 -1
  17. package/build-module/actions.js +27 -0
  18. package/build-module/actions.js.map +1 -1
  19. package/build-module/batch/default-processor.js +12 -5
  20. package/build-module/batch/default-processor.js.map +1 -1
  21. package/build-module/hooks/use-resource-permissions.js +82 -0
  22. package/build-module/hooks/use-resource-permissions.js.map +1 -0
  23. package/build-module/index.js +3 -0
  24. package/build-module/index.js.map +1 -1
  25. package/build-module/reducer.js +5 -2
  26. package/build-module/reducer.js.map +1 -1
  27. package/build-module/resolvers.js +10 -18
  28. package/build-module/resolvers.js.map +1 -1
  29. package/build-module/selectors.js +6 -4
  30. package/build-module/selectors.js.map +1 -1
  31. package/package.json +11 -11
  32. package/src/actions.js +27 -0
  33. package/src/batch/default-processor.js +10 -5
  34. package/src/hooks/test/use-resource-permissions.js +115 -0
  35. package/src/hooks/use-resource-permissions.ts +120 -0
  36. package/src/index.js +3 -0
  37. package/src/reducer.js +9 -2
  38. package/src/resolvers.js +10 -18
  39. package/src/selectors.ts +6 -4
  40. package/src/test/reducer.js +5 -4
@@ -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';
package/src/reducer.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { keyBy, map, groupBy, flowRight, isEqual, get } from 'lodash';
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
- ...keyBy( action.users, 'id' ),
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,
package/src/resolvers.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { find, includes, get, compact, uniq, map, mapKeys } from 'lodash';
4
+ import { camelCase, find, get, includes, map, mapKeys, uniq } from 'lodash';
5
5
 
6
6
  /**
7
7
  * WordPress dependencies
@@ -50,7 +50,8 @@ export const getCurrentUser =
50
50
  * @param {string} name Entity name.
51
51
  * @param {number|string} key Record's key
52
52
  * @param {Object|undefined} query Optional object of query parameters to
53
- * include with request.
53
+ * include with request. If requesting specific
54
+ * fields, fields must always include the ID.
54
55
  */
55
56
  export const getEntityRecord =
56
57
  ( kind, name, key = '', query ) =>
@@ -131,7 +132,8 @@ export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
131
132
  *
132
133
  * @param {string} kind Entity kind.
133
134
  * @param {string} name Entity name.
134
- * @param {Object?} query Query Object.
135
+ * @param {Object?} query Query Object. If requesting specific fields, fields
136
+ * must always include the ID.
135
137
  */
136
138
  export const getEntityRecords =
137
139
  ( kind, name, query = {} ) =>
@@ -304,7 +306,7 @@ export const canUser =
304
306
  // return the expected result in the native version. Instead, API requests
305
307
  // only return the result, without including response properties like the headers.
306
308
  const allowHeader = response.headers?.get( 'allow' );
307
- const key = compact( [ action, resource, id ] ).join( '/' );
309
+ const key = [ action, resource, id ].filter( Boolean ).join( '/' );
308
310
  const isAllowed = includes( allowHeader, method );
309
311
  dispatch.receiveUserPermission( key, isAllowed );
310
312
  };
@@ -339,11 +341,10 @@ export const canUserEditEntityRecord =
339
341
  export const getAutosaves =
340
342
  ( postType, postId ) =>
341
343
  async ( { dispatch, resolveSelect } ) => {
342
- const { rest_base: restBase } = await resolveSelect.getPostType(
343
- postType
344
- );
344
+ const { rest_base: restBase, rest_namespace: restNamespace = 'wp/v2' } =
345
+ await resolveSelect.getPostType( postType );
345
346
  const autosaves = await apiFetch( {
346
- path: `/wp/v2/${ restBase }/${ postId }/autosaves?context=edit`,
347
+ path: `/${ restNamespace }/${ restBase }/${ postId }/autosaves?context=edit`,
347
348
  } );
348
349
 
349
350
  if ( autosaves && autosaves.length ) {
@@ -476,16 +477,7 @@ export const getBlockPatterns =
476
477
  path: '/wp/v2/block-patterns/patterns',
477
478
  } );
478
479
  const patterns = map( restPatterns, ( pattern ) =>
479
- mapKeys( pattern, ( value, key ) => {
480
- switch ( key ) {
481
- case 'block_types':
482
- return 'blockTypes';
483
- case 'viewport_width':
484
- return 'viewportWidth';
485
- default:
486
- return key;
487
- }
488
- } )
480
+ mapKeys( pattern, ( value, key ) => camelCase( key ) )
489
481
  );
490
482
  dispatch( { type: 'RECEIVE_BLOCK_PATTERNS', patterns } );
491
483
  };
package/src/selectors.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import createSelector from 'rememo';
5
- import { set, map, find, get, filter, compact } from 'lodash';
5
+ import { set, map, find, get, filter } from 'lodash';
6
6
 
7
7
  /**
8
8
  * WordPress dependencies
@@ -296,7 +296,8 @@ interface GetEntityRecord {
296
296
  * @param kind Entity kind.
297
297
  * @param name Entity name.
298
298
  * @param key Record's key
299
- * @param query Optional query.
299
+ * @param query Optional query. If requesting specific
300
+ * fields, fields must always include the ID.
300
301
  *
301
302
  * @return Record.
302
303
  */
@@ -526,7 +527,8 @@ interface GetEntityRecords {
526
527
  * @param state State tree
527
528
  * @param kind Entity kind.
528
529
  * @param name Entity name.
529
- * @param query Optional terms query.
530
+ * @param query Optional terms query. If requesting specific
531
+ * fields, fields must always include the ID.
530
532
  *
531
533
  * @return Records.
532
534
  */
@@ -1115,7 +1117,7 @@ export function canUser(
1115
1117
  resource: string,
1116
1118
  id?: GenericRecordKey
1117
1119
  ): boolean | undefined {
1118
- const key = compact( [ action, resource, id ] ).join( '/' );
1120
+ const key = [ action, resource, id ].filter( Boolean ).join( '/' );
1119
1121
  return get( state, [ 'userPermissions', key ] );
1120
1122
  }
1121
1123
 
@@ -2,7 +2,6 @@
2
2
  * External dependencies
3
3
  */
4
4
  import deepFreeze from 'deep-freeze';
5
- import { filter } from 'lodash';
6
5
 
7
6
  /**
8
7
  * Internal dependencies
@@ -135,9 +134,11 @@ describe( 'entities', () => {
135
134
  entities: [ { kind: 'postType', name: 'posts' } ],
136
135
  } );
137
136
 
138
- expect( filter( state.config, { kind: 'postType' } ) ).toEqual( [
139
- { kind: 'postType', name: 'posts' },
140
- ] );
137
+ expect(
138
+ Object.entries( state.config )
139
+ .filter( ( [ , cfg ] ) => cfg.kind === 'postType' )
140
+ .map( ( [ , cfg ] ) => cfg )
141
+ ).toEqual( [ { kind: 'postType', name: 'posts' } ] );
141
142
  } );
142
143
  } );
143
144