@wordpress/core-data 4.0.1-next.253d9b6e21.0 → 4.0.3
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/README.md +11 -3
- package/build/actions.js +124 -117
- package/build/actions.js.map +1 -1
- package/build/batch/default-processor.js +58 -27
- package/build/batch/default-processor.js.map +1 -1
- package/build/entities.js +24 -18
- package/build/entities.js.map +1 -1
- package/build/index.js +9 -17
- package/build/index.js.map +1 -1
- package/build/locks/actions.js +17 -77
- package/build/locks/actions.js.map +1 -1
- package/build/locks/engine.js +77 -0
- package/build/locks/engine.js.map +1 -0
- package/build/locks/reducer.js +1 -5
- package/build/locks/reducer.js.map +1 -1
- package/build/locks/selectors.js +6 -6
- package/build/locks/selectors.js.map +1 -1
- package/build/queried-data/get-query-parts.js +9 -4
- package/build/queried-data/get-query-parts.js.map +1 -1
- package/build/queried-data/selectors.js +3 -9
- package/build/queried-data/selectors.js.map +1 -1
- package/build/reducer.js +1 -4
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +120 -91
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +31 -11
- package/build/selectors.js.map +1 -1
- package/build/utils/if-not-resolved.js +6 -21
- package/build/utils/if-not-resolved.js.map +1 -1
- package/build/utils/index.js +8 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/is-raw-attribute.js +19 -0
- package/build/utils/is-raw-attribute.js.map +1 -0
- package/build-module/actions.js +106 -107
- package/build-module/actions.js.map +1 -1
- package/build-module/batch/default-processor.js +57 -27
- package/build-module/batch/default-processor.js.map +1 -1
- package/build-module/entities.js +19 -14
- package/build-module/entities.js.map +1 -1
- package/build-module/index.js +10 -14
- package/build-module/index.js.map +1 -1
- package/build-module/locks/actions.js +14 -68
- package/build-module/locks/actions.js.map +1 -1
- package/build-module/locks/engine.js +66 -0
- package/build-module/locks/engine.js.map +1 -0
- package/build-module/locks/reducer.js +1 -2
- package/build-module/locks/reducer.js.map +1 -1
- package/build-module/locks/selectors.js +4 -4
- package/build-module/locks/selectors.js.map +1 -1
- package/build-module/queried-data/get-query-parts.js +9 -4
- package/build-module/queried-data/get-query-parts.js.map +1 -1
- package/build-module/queried-data/selectors.js +3 -9
- package/build-module/queried-data/selectors.js.map +1 -1
- package/build-module/reducer.js +1 -3
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +94 -74
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +30 -10
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/if-not-resolved.js +6 -19
- package/build-module/utils/if-not-resolved.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/is-raw-attribute.js +12 -0
- package/build-module/utils/is-raw-attribute.js.map +1 -0
- package/package.json +11 -12
- package/src/actions.js +112 -189
- package/src/batch/default-processor.js +57 -26
- package/src/batch/test/default-processor.js +53 -26
- package/src/entities.js +15 -16
- package/src/index.js +7 -10
- package/src/locks/actions.js +10 -61
- package/src/locks/engine.js +43 -0
- package/src/locks/reducer.js +1 -3
- package/src/locks/selectors.js +4 -4
- package/src/locks/test/engine.js +135 -0
- package/src/locks/test/reducer.js +1 -1
- package/src/locks/test/selectors.js +105 -124
- package/src/queried-data/get-query-parts.js +11 -6
- package/src/queried-data/selectors.js +2 -9
- package/src/queried-data/test/get-query-parts.js +1 -1
- package/src/queried-data/test/selectors.js +1 -0
- package/src/reducer.js +0 -2
- package/src/resolvers.js +86 -106
- package/src/selectors.js +113 -40
- package/src/test/actions.js +243 -172
- package/src/test/entities.js +40 -26
- package/src/test/resolvers.js +270 -223
- package/src/test/selectors.js +71 -0
- package/src/utils/if-not-resolved.js +8 -26
- package/src/utils/index.js +1 -0
- package/src/utils/is-raw-attribute.js +11 -0
- package/src/utils/test/if-not-resolved.js +28 -27
- package/src/utils/test/is-raw-attribute.js +22 -0
- package/build/controls.js +0 -44
- package/build/controls.js.map +0 -1
- package/build/locks/index.js +0 -47
- package/build/locks/index.js.map +0 -1
- package/build-module/controls.js +0 -31
- package/build-module/controls.js.map +0 -1
- package/build-module/locks/index.js +0 -4
- package/build-module/locks/index.js.map +0 -1
- package/src/controls.js +0 -31
- package/src/locks/index.js +0 -3
- package/src/locks/test/actions.js +0 -307
- package/src/test/integration.js +0 -264
|
@@ -11,6 +11,18 @@ import defaultProcessor from '../default-processor';
|
|
|
11
11
|
jest.mock( '@wordpress/api-fetch' );
|
|
12
12
|
|
|
13
13
|
describe( 'defaultProcessor', () => {
|
|
14
|
+
const preflightResponse = {
|
|
15
|
+
endpoints: [
|
|
16
|
+
{
|
|
17
|
+
args: {
|
|
18
|
+
requests: {
|
|
19
|
+
maxItems: 25,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
14
26
|
const requests = [
|
|
15
27
|
{
|
|
16
28
|
path: '/v1/cricketers',
|
|
@@ -26,7 +38,12 @@ describe( 'defaultProcessor', () => {
|
|
|
26
38
|
},
|
|
27
39
|
];
|
|
28
40
|
|
|
29
|
-
const
|
|
41
|
+
const expectedPreflightOptions = {
|
|
42
|
+
path: '/batch/v1',
|
|
43
|
+
method: 'OPTIONS',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const expectedBatchOptions = {
|
|
30
47
|
path: '/batch/v1',
|
|
31
48
|
method: 'POST',
|
|
32
49
|
data: {
|
|
@@ -49,21 +66,26 @@ describe( 'defaultProcessor', () => {
|
|
|
49
66
|
};
|
|
50
67
|
|
|
51
68
|
it( 'handles a successful request', async () => {
|
|
52
|
-
apiFetch.mockImplementation( async () =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
apiFetch.mockImplementation( async ( { method } ) =>
|
|
70
|
+
method === 'OPTIONS'
|
|
71
|
+
? preflightResponse
|
|
72
|
+
: {
|
|
73
|
+
failed: false,
|
|
74
|
+
responses: [
|
|
75
|
+
{
|
|
76
|
+
status: 200,
|
|
77
|
+
body: 'Lyon',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
status: 400,
|
|
81
|
+
body: 'Error!',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
}
|
|
85
|
+
);
|
|
65
86
|
const results = await defaultProcessor( requests );
|
|
66
|
-
expect( apiFetch ).toHaveBeenCalledWith(
|
|
87
|
+
expect( apiFetch ).toHaveBeenCalledWith( expectedPreflightOptions );
|
|
88
|
+
expect( apiFetch ).toHaveBeenCalledWith( expectedBatchOptions );
|
|
67
89
|
expect( results ).toEqual( [
|
|
68
90
|
{ output: 'Lyon' },
|
|
69
91
|
{ error: 'Error!' },
|
|
@@ -71,18 +93,23 @@ describe( 'defaultProcessor', () => {
|
|
|
71
93
|
} );
|
|
72
94
|
|
|
73
95
|
it( 'handles a failed request', async () => {
|
|
74
|
-
apiFetch.mockImplementation( async () =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
apiFetch.mockImplementation( async ( { method } ) =>
|
|
97
|
+
method === 'OPTIONS'
|
|
98
|
+
? preflightResponse
|
|
99
|
+
: {
|
|
100
|
+
failed: true,
|
|
101
|
+
responses: [
|
|
102
|
+
null,
|
|
103
|
+
{
|
|
104
|
+
status: 400,
|
|
105
|
+
body: 'Error!',
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
}
|
|
109
|
+
);
|
|
84
110
|
const results = await defaultProcessor( requests );
|
|
85
|
-
expect( apiFetch ).toHaveBeenCalledWith(
|
|
111
|
+
expect( apiFetch ).toHaveBeenCalledWith( expectedPreflightOptions );
|
|
112
|
+
expect( apiFetch ).toHaveBeenCalledWith( expectedBatchOptions );
|
|
86
113
|
expect( results ).toEqual( [
|
|
87
114
|
{ error: undefined },
|
|
88
115
|
{ error: 'Error!' },
|
package/src/entities.js
CHANGED
|
@@ -6,18 +6,18 @@ import { upperFirst, camelCase, map, find, get, startCase } from 'lodash';
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
10
|
-
import { apiFetch } from '@wordpress/data-controls';
|
|
9
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
11
10
|
import { __ } from '@wordpress/i18n';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Internal dependencies
|
|
15
14
|
*/
|
|
16
15
|
import { addEntities } from './actions';
|
|
17
|
-
import { STORE_NAME } from './name';
|
|
18
16
|
|
|
19
17
|
export const DEFAULT_ENTITY_KEY = 'id';
|
|
20
18
|
|
|
19
|
+
const POST_RAW_ATTRIBUTES = [ 'title', 'excerpt', 'content' ];
|
|
20
|
+
|
|
21
21
|
export const defaultEntities = [
|
|
22
22
|
{
|
|
23
23
|
label: __( 'Base' ),
|
|
@@ -41,6 +41,7 @@ export const defaultEntities = [
|
|
|
41
41
|
key: 'slug',
|
|
42
42
|
baseURL: '/wp/v2/types',
|
|
43
43
|
baseURLParams: { context: 'edit' },
|
|
44
|
+
rawAttributes: POST_RAW_ATTRIBUTES,
|
|
44
45
|
},
|
|
45
46
|
{
|
|
46
47
|
name: 'media',
|
|
@@ -115,6 +116,7 @@ export const defaultEntities = [
|
|
|
115
116
|
baseURLParams: { context: 'edit' },
|
|
116
117
|
plural: 'menuItems',
|
|
117
118
|
label: __( 'Menu Item' ),
|
|
119
|
+
rawAttributes: [ 'title', 'content' ],
|
|
118
120
|
},
|
|
119
121
|
{
|
|
120
122
|
name: 'menuLocation',
|
|
@@ -167,8 +169,8 @@ export const prePersistPostType = ( persistedRecord, edits ) => {
|
|
|
167
169
|
*
|
|
168
170
|
* @return {Promise} Entities promise
|
|
169
171
|
*/
|
|
170
|
-
function
|
|
171
|
-
const postTypes =
|
|
172
|
+
async function loadPostTypeEntities() {
|
|
173
|
+
const postTypes = await apiFetch( { path: '/wp/v2/types?context=edit' } );
|
|
172
174
|
return map( postTypes, ( postType, name ) => {
|
|
173
175
|
const isTemplate = [ 'wp_template', 'wp_template_part' ].includes(
|
|
174
176
|
name
|
|
@@ -184,6 +186,7 @@ function* loadPostTypeEntities() {
|
|
|
184
186
|
selection: true,
|
|
185
187
|
},
|
|
186
188
|
mergedEdits: { meta: true },
|
|
189
|
+
rawAttributes: POST_RAW_ATTRIBUTES,
|
|
187
190
|
getTitle: ( record ) =>
|
|
188
191
|
record?.title?.rendered ||
|
|
189
192
|
record?.title ||
|
|
@@ -199,8 +202,8 @@ function* loadPostTypeEntities() {
|
|
|
199
202
|
*
|
|
200
203
|
* @return {Promise} Entities promise
|
|
201
204
|
*/
|
|
202
|
-
function
|
|
203
|
-
const taxonomies =
|
|
205
|
+
async function loadTaxonomyEntities() {
|
|
206
|
+
const taxonomies = await apiFetch( {
|
|
204
207
|
path: '/wp/v2/taxonomies?context=edit',
|
|
205
208
|
} );
|
|
206
209
|
return map( taxonomies, ( taxonomy, name ) => {
|
|
@@ -248,12 +251,8 @@ export const getMethodName = (
|
|
|
248
251
|
*
|
|
249
252
|
* @return {Array} Entities
|
|
250
253
|
*/
|
|
251
|
-
export
|
|
252
|
-
let entities =
|
|
253
|
-
STORE_NAME,
|
|
254
|
-
'getEntitiesByKind',
|
|
255
|
-
kind
|
|
256
|
-
);
|
|
254
|
+
export const getKindEntities = ( kind ) => async ( { select, dispatch } ) => {
|
|
255
|
+
let entities = select.getEntitiesByKind( kind );
|
|
257
256
|
if ( entities && entities.length !== 0 ) {
|
|
258
257
|
return entities;
|
|
259
258
|
}
|
|
@@ -263,8 +262,8 @@ export function* getKindEntities( kind ) {
|
|
|
263
262
|
return [];
|
|
264
263
|
}
|
|
265
264
|
|
|
266
|
-
entities =
|
|
267
|
-
|
|
265
|
+
entities = await kindConfig.loadEntities();
|
|
266
|
+
dispatch( addEntities( entities ) );
|
|
268
267
|
|
|
269
268
|
return entities;
|
|
270
|
-
}
|
|
269
|
+
};
|
package/src/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { createReduxStore, register } from '@wordpress/data';
|
|
5
|
-
import { controls } from '@wordpress/data-controls';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Internal dependencies
|
|
@@ -11,9 +10,7 @@ import reducer from './reducer';
|
|
|
11
10
|
import * as selectors from './selectors';
|
|
12
11
|
import * as actions from './actions';
|
|
13
12
|
import * as resolvers from './resolvers';
|
|
14
|
-
import
|
|
15
|
-
import * as locksActions from './locks/actions';
|
|
16
|
-
import customControls from './controls';
|
|
13
|
+
import createLocksActions from './locks/actions';
|
|
17
14
|
import { defaultEntities, getMethodName } from './entities';
|
|
18
15
|
import { STORE_NAME } from './name';
|
|
19
16
|
|
|
@@ -57,13 +54,13 @@ const entityActions = defaultEntities.reduce( ( result, entity ) => {
|
|
|
57
54
|
return result;
|
|
58
55
|
}, {} );
|
|
59
56
|
|
|
60
|
-
const storeConfig = {
|
|
57
|
+
const storeConfig = () => ( {
|
|
61
58
|
reducer,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
selectors: { ...selectors, ...entitySelectors, ...locksSelectors },
|
|
59
|
+
actions: { ...actions, ...entityActions, ...createLocksActions() },
|
|
60
|
+
selectors: { ...selectors, ...entitySelectors },
|
|
65
61
|
resolvers: { ...resolvers, ...entityResolvers },
|
|
66
|
-
|
|
62
|
+
__experimentalUseThunks: true,
|
|
63
|
+
} );
|
|
67
64
|
|
|
68
65
|
/**
|
|
69
66
|
* Store definition for the code data namespace.
|
|
@@ -72,7 +69,7 @@ const storeConfig = {
|
|
|
72
69
|
*
|
|
73
70
|
* @type {Object}
|
|
74
71
|
*/
|
|
75
|
-
export const store = createReduxStore( STORE_NAME, storeConfig );
|
|
72
|
+
export const store = createReduxStore( STORE_NAME, storeConfig() );
|
|
76
73
|
|
|
77
74
|
register( store );
|
|
78
75
|
|
package/src/locks/actions.js
CHANGED
|
@@ -1,69 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WordPress dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { __unstableAwaitPromise } from '@wordpress/data-controls';
|
|
5
|
-
import { controls } from '@wordpress/data';
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* Internal dependencies
|
|
9
3
|
*/
|
|
10
|
-
import
|
|
4
|
+
import createLocks from './engine';
|
|
11
5
|
|
|
12
|
-
export function
|
|
13
|
-
const
|
|
14
|
-
exclusive,
|
|
15
|
-
} );
|
|
16
|
-
yield* __unstableProcessPendingLockRequests();
|
|
17
|
-
return yield __unstableAwaitPromise( promise );
|
|
18
|
-
}
|
|
6
|
+
export default function createLocksActions() {
|
|
7
|
+
const locks = createLocks();
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
notifyAcquired = resolve;
|
|
24
|
-
} );
|
|
25
|
-
yield {
|
|
26
|
-
type: 'ENQUEUE_LOCK_REQUEST',
|
|
27
|
-
request: { store, path, exclusive, notifyAcquired },
|
|
28
|
-
};
|
|
29
|
-
return promise;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function* __unstableReleaseStoreLock( lock ) {
|
|
33
|
-
yield {
|
|
34
|
-
type: 'RELEASE_LOCK',
|
|
35
|
-
lock,
|
|
36
|
-
};
|
|
37
|
-
yield* __unstableProcessPendingLockRequests();
|
|
38
|
-
}
|
|
9
|
+
function __unstableAcquireStoreLock( store, path, { exclusive } ) {
|
|
10
|
+
return () => locks.acquire( store, path, exclusive );
|
|
11
|
+
}
|
|
39
12
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type: 'PROCESS_PENDING_LOCK_REQUESTS',
|
|
43
|
-
};
|
|
44
|
-
const lockRequests = yield controls.select(
|
|
45
|
-
STORE_NAME,
|
|
46
|
-
'__unstableGetPendingLockRequests'
|
|
47
|
-
);
|
|
48
|
-
for ( const request of lockRequests ) {
|
|
49
|
-
const { store, path, exclusive, notifyAcquired } = request;
|
|
50
|
-
const isAvailable = yield controls.select(
|
|
51
|
-
STORE_NAME,
|
|
52
|
-
'__unstableIsLockAvailable',
|
|
53
|
-
store,
|
|
54
|
-
path,
|
|
55
|
-
{
|
|
56
|
-
exclusive,
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
if ( isAvailable ) {
|
|
60
|
-
const lock = { store, path, exclusive };
|
|
61
|
-
yield {
|
|
62
|
-
type: 'GRANT_LOCK_REQUEST',
|
|
63
|
-
lock,
|
|
64
|
-
request,
|
|
65
|
-
};
|
|
66
|
-
notifyAcquired( lock );
|
|
67
|
-
}
|
|
13
|
+
function __unstableReleaseStoreLock( lock ) {
|
|
14
|
+
return () => locks.release( lock );
|
|
68
15
|
}
|
|
16
|
+
|
|
17
|
+
return { __unstableAcquireStoreLock, __unstableReleaseStoreLock };
|
|
69
18
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import reducer from './reducer';
|
|
5
|
+
import { isLockAvailable, getPendingLockRequests } from './selectors';
|
|
6
|
+
|
|
7
|
+
export default function createLocks() {
|
|
8
|
+
let state = reducer( undefined, { type: '@@INIT' } );
|
|
9
|
+
|
|
10
|
+
function processPendingLockRequests() {
|
|
11
|
+
for ( const request of getPendingLockRequests( state ) ) {
|
|
12
|
+
const { store, path, exclusive, notifyAcquired } = request;
|
|
13
|
+
if ( isLockAvailable( state, store, path, { exclusive } ) ) {
|
|
14
|
+
const lock = { store, path, exclusive };
|
|
15
|
+
state = reducer( state, {
|
|
16
|
+
type: 'GRANT_LOCK_REQUEST',
|
|
17
|
+
lock,
|
|
18
|
+
request,
|
|
19
|
+
} );
|
|
20
|
+
notifyAcquired( lock );
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function acquire( store, path, exclusive ) {
|
|
26
|
+
return new Promise( ( resolve ) => {
|
|
27
|
+
state = reducer( state, {
|
|
28
|
+
type: 'ENQUEUE_LOCK_REQUEST',
|
|
29
|
+
request: { store, path, exclusive, notifyAcquired: resolve },
|
|
30
|
+
} );
|
|
31
|
+
processPendingLockRequests();
|
|
32
|
+
} );
|
|
33
|
+
}
|
|
34
|
+
function release( lock ) {
|
|
35
|
+
state = reducer( state, {
|
|
36
|
+
type: 'RELEASE_LOCK',
|
|
37
|
+
lock,
|
|
38
|
+
} );
|
|
39
|
+
processPendingLockRequests();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { acquire, release };
|
|
43
|
+
}
|
package/src/locks/reducer.js
CHANGED
|
@@ -19,7 +19,7 @@ const DEFAULT_STATE = {
|
|
|
19
19
|
*
|
|
20
20
|
* @return {Object} Updated state.
|
|
21
21
|
*/
|
|
22
|
-
export function locks( state = DEFAULT_STATE, action ) {
|
|
22
|
+
export default function locks( state = DEFAULT_STATE, action ) {
|
|
23
23
|
switch ( action.type ) {
|
|
24
24
|
case 'ENQUEUE_LOCK_REQUEST': {
|
|
25
25
|
const { request } = action;
|
|
@@ -60,5 +60,3 @@ export function locks( state = DEFAULT_STATE, action ) {
|
|
|
60
60
|
|
|
61
61
|
return state;
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
export default locks;
|
package/src/locks/selectors.js
CHANGED
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
getNode,
|
|
9
9
|
} from './utils';
|
|
10
10
|
|
|
11
|
-
export function
|
|
12
|
-
return state.
|
|
11
|
+
export function getPendingLockRequests( state ) {
|
|
12
|
+
return state.requests;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function
|
|
15
|
+
export function isLockAvailable( state, store, path, { exclusive } ) {
|
|
16
16
|
const storePath = [ store, ...path ];
|
|
17
|
-
const locks = state.
|
|
17
|
+
const locks = state.tree;
|
|
18
18
|
|
|
19
19
|
// Validate all parents and the node itself
|
|
20
20
|
for ( const node of iteratePath( locks, storePath ) ) {
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import createLocks from '../engine';
|
|
5
|
+
|
|
6
|
+
jest.useRealTimers();
|
|
7
|
+
|
|
8
|
+
// we correctly await all promises with expect calls, but the rule doesn't detect that
|
|
9
|
+
/* eslint-disable jest/valid-expect-in-promise */
|
|
10
|
+
|
|
11
|
+
describe( 'Locks engine', () => {
|
|
12
|
+
it( 'does not grant two exclusive locks at once', async () => {
|
|
13
|
+
const locks = createLocks();
|
|
14
|
+
|
|
15
|
+
let l1Granted = false;
|
|
16
|
+
let l2Granted = false;
|
|
17
|
+
|
|
18
|
+
// request two locks
|
|
19
|
+
const l1 = locks.acquire( 'store', [ 'root' ], true );
|
|
20
|
+
const l2 = locks.acquire( 'store', [ 'root' ], true );
|
|
21
|
+
|
|
22
|
+
// on each grant, verify that the other lock is not granted at the same time
|
|
23
|
+
const check1 = l1.then( () => {
|
|
24
|
+
l1Granted = true;
|
|
25
|
+
expect( l2Granted ).toBe( false );
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
const check2 = l2.then( () => {
|
|
29
|
+
l2Granted = true;
|
|
30
|
+
expect( l1Granted ).toBe( false );
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
// unlock both
|
|
34
|
+
const lock1 = await l1;
|
|
35
|
+
locks.release( lock1 );
|
|
36
|
+
l1Granted = false;
|
|
37
|
+
|
|
38
|
+
const lock2 = await l2;
|
|
39
|
+
locks.release( lock2 );
|
|
40
|
+
l2Granted = false;
|
|
41
|
+
|
|
42
|
+
// ensure that both locks were granted and checked
|
|
43
|
+
return await Promise.all( [ check1, check2 ] );
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
it( 'does not grant an exclusive lock if a non-exclusive one already exists', async () => {
|
|
47
|
+
const locks = createLocks();
|
|
48
|
+
|
|
49
|
+
let l1Granted = false;
|
|
50
|
+
let l2Granted = false;
|
|
51
|
+
|
|
52
|
+
// request two locks
|
|
53
|
+
const l1 = locks.acquire( 'store', [ 'root' ], false );
|
|
54
|
+
const l2 = locks.acquire( 'store', [ 'root' ], true );
|
|
55
|
+
|
|
56
|
+
// on each grant, verify that the other lock is not granted at the same time
|
|
57
|
+
const check1 = l1.then( () => {
|
|
58
|
+
l1Granted = true;
|
|
59
|
+
expect( l2Granted ).toBe( false );
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
const check2 = l2.then( () => {
|
|
63
|
+
l2Granted = true;
|
|
64
|
+
expect( l1Granted ).toBe( false );
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
// unlock both
|
|
68
|
+
const lock1 = await l1;
|
|
69
|
+
locks.release( lock1 );
|
|
70
|
+
l1Granted = false;
|
|
71
|
+
|
|
72
|
+
const lock2 = await l2;
|
|
73
|
+
locks.release( lock2 );
|
|
74
|
+
l2Granted = false;
|
|
75
|
+
|
|
76
|
+
// ensure that both locks were granted and checked
|
|
77
|
+
return await Promise.all( [ check1, check2 ] );
|
|
78
|
+
} );
|
|
79
|
+
|
|
80
|
+
it( 'does not grant two exclusive locks to parent and child', async () => {
|
|
81
|
+
const locks = createLocks();
|
|
82
|
+
|
|
83
|
+
let l1Granted = false;
|
|
84
|
+
let l2Granted = false;
|
|
85
|
+
|
|
86
|
+
// request two locks
|
|
87
|
+
const l1 = locks.acquire( 'store', [ 'root' ], true );
|
|
88
|
+
const l2 = locks.acquire( 'store', [ 'root', 'child' ], true );
|
|
89
|
+
|
|
90
|
+
// on each grant, verify that the other lock is not granted at the same time
|
|
91
|
+
const check1 = l1.then( () => {
|
|
92
|
+
l1Granted = true;
|
|
93
|
+
expect( l2Granted ).toBe( false );
|
|
94
|
+
} );
|
|
95
|
+
|
|
96
|
+
const check2 = l2.then( () => {
|
|
97
|
+
l2Granted = true;
|
|
98
|
+
expect( l1Granted ).toBe( false );
|
|
99
|
+
} );
|
|
100
|
+
|
|
101
|
+
// unlock both
|
|
102
|
+
const lock1 = await l1;
|
|
103
|
+
locks.release( lock1 );
|
|
104
|
+
l1Granted = false;
|
|
105
|
+
|
|
106
|
+
const lock2 = await l2;
|
|
107
|
+
locks.release( lock2 );
|
|
108
|
+
l2Granted = false;
|
|
109
|
+
|
|
110
|
+
// ensure that both locks were granted and checked
|
|
111
|
+
return await Promise.all( [ check1, check2 ] );
|
|
112
|
+
} );
|
|
113
|
+
|
|
114
|
+
it( 'grants two non-exclusive locks at once', async () => {
|
|
115
|
+
const locks = createLocks();
|
|
116
|
+
|
|
117
|
+
const l1 = await locks.acquire( 'store', [ 'root' ], false );
|
|
118
|
+
const l2 = await locks.acquire( 'store', [ 'root' ], false );
|
|
119
|
+
|
|
120
|
+
expect( l1 ).not.toBeUndefined();
|
|
121
|
+
expect( l2 ).not.toBeUndefined();
|
|
122
|
+
} );
|
|
123
|
+
|
|
124
|
+
it( 'grants two exclusive locks to different branches', async () => {
|
|
125
|
+
const locks = createLocks();
|
|
126
|
+
|
|
127
|
+
const l1 = await locks.acquire( 'store', [ 'a' ], true );
|
|
128
|
+
const l2 = await locks.acquire( 'store', [ 'b' ], true );
|
|
129
|
+
|
|
130
|
+
expect( l1 ).not.toBeUndefined();
|
|
131
|
+
expect( l2 ).not.toBeUndefined();
|
|
132
|
+
} );
|
|
133
|
+
} );
|
|
134
|
+
|
|
135
|
+
/* eslint-enable jest/valid-expect-in-promise */
|