Skip to content

Commit 12d81cd

Browse files
authored
Replace batch processing store with a simpler API (#28210)
- Replaces the batch-processing data store with a stripped back API that doesn't use any persistence. - Adds __experimentalBatch() to core-data. - Changes edit-widgets to use __experimentalBatch(). - Fixes batch processing race condition by using batch.add( thunk ).
1 parent b0d4e1f commit 12d81cd

File tree

20 files changed

+714
-855
lines changed

20 files changed

+714
-855
lines changed

docs/designers-developers/developers/data/data-core.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ _Parameters_
531531
- _name_ `string`: Name of the deleted entity.
532532
- _recordId_ `string`: Record ID of the deleted entity.
533533
- _query_ `?Object`: Special query parameters for the DELETE API call.
534+
- _options_ `[Object]`: Delete options.
535+
- _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a control descriptor.
534536

535537
<a name="editEntityRecord" href="#editEntityRecord">#</a> **editEntityRecord**
536538

@@ -697,6 +699,7 @@ _Parameters_
697699
- _record_ `Object`: Record to be saved.
698700
- _options_ `Object`: Saving options.
699701
- _options.isAutosave_ `[boolean]`: Whether this is an autosave.
702+
- _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a control descriptor.
700703

701704
<a name="undo" href="#undo">#</a> **undo**
702705

packages/core-data/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ _Parameters_
6464
- _name_ `string`: Name of the deleted entity.
6565
- _recordId_ `string`: Record ID of the deleted entity.
6666
- _query_ `?Object`: Special query parameters for the DELETE API call.
67+
- _options_ `[Object]`: Delete options.
68+
- _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a control descriptor.
6769

6870
<a name="editEntityRecord" href="#editEntityRecord">#</a> **editEntityRecord**
6971

@@ -230,6 +232,7 @@ _Parameters_
230232
- _record_ `Object`: Record to be saved.
231233
- _options_ `Object`: Saving options.
232234
- _options.isAutosave_ `[boolean]`: Whether this is an autosave.
235+
- _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a control descriptor.
233236

234237
<a name="undo" href="#undo">#</a> **undo**
235238

packages/core-data/src/actions.js

Lines changed: 126 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { v4 as uuid } from 'uuid';
88
* WordPress dependencies
99
*/
1010
import { controls } from '@wordpress/data';
11-
import { apiFetch } from '@wordpress/data-controls';
11+
import { apiFetch, __unstableAwaitPromise } from '@wordpress/data-controls';
1212
import { addQueryArgs } from '@wordpress/url';
1313

1414
/**
@@ -20,6 +20,8 @@ import {
2020
__unstableAcquireStoreLock,
2121
__unstableReleaseStoreLock,
2222
} from './locks';
23+
import { createBatch } from './batch';
24+
import { getDispatch } from './controls';
2325

2426
/**
2527
* Returns an action object used in signalling that authors have been received.
@@ -154,12 +156,23 @@ export function receiveEmbedPreview( url, preview ) {
154156
/**
155157
* Action triggered to delete an entity record.
156158
*
157-
* @param {string} kind Kind of the deleted entity.
158-
* @param {string} name Name of the deleted entity.
159-
* @param {string} recordId Record ID of the deleted entity.
160-
* @param {?Object} query Special query parameters for the DELETE API call.
159+
* @param {string} kind Kind of the deleted entity.
160+
* @param {string} name Name of the deleted entity.
161+
* @param {string} recordId Record ID of the deleted entity.
162+
* @param {?Object} query Special query parameters for the
163+
* DELETE API call.
164+
* @param {Object} [options] Delete options.
165+
* @param {Function} [options.__unstableFetch] Internal use only. Function to
166+
* call instead of `apiFetch()`.
167+
* Must return a control descriptor.
161168
*/
162-
export function* deleteEntityRecord( kind, name, recordId, query ) {
169+
export function* deleteEntityRecord(
170+
kind,
171+
name,
172+
recordId,
173+
query,
174+
{ __unstableFetch = null } = {}
175+
) {
163176
const entities = yield getKindEntities( kind );
164177
const entity = find( entities, { kind, name } );
165178
let error;
@@ -188,10 +201,17 @@ export function* deleteEntityRecord( kind, name, recordId, query ) {
188201
path = addQueryArgs( path, query );
189202
}
190203

191-
deletedRecord = yield apiFetch( {
204+
const options = {
192205
path,
193206
method: 'DELETE',
194-
} );
207+
};
208+
if ( __unstableFetch ) {
209+
deletedRecord = yield __unstableAwaitPromise(
210+
__unstableFetch( options )
211+
);
212+
} else {
213+
deletedRecord = yield apiFetch( options );
214+
}
195215

196216
yield removeItems( kind, name, recordId, true );
197217
} catch ( _error ) {
@@ -329,17 +349,21 @@ export function __unstableCreateUndoLevel() {
329349
/**
330350
* Action triggered to save an entity record.
331351
*
332-
* @param {string} kind Kind of the received entity.
333-
* @param {string} name Name of the received entity.
334-
* @param {Object} record Record to be saved.
335-
* @param {Object} options Saving options.
336-
* @param {boolean} [options.isAutosave=false] Whether this is an autosave.
352+
* @param {string} kind Kind of the received entity.
353+
* @param {string} name Name of the received entity.
354+
* @param {Object} record Record to be saved.
355+
* @param {Object} options Saving options.
356+
* @param {boolean} [options.isAutosave=false] Whether this is an autosave.
357+
* @param {Function} [options.__unstableFetch] Internal use only. Function to
358+
* call instead of `apiFetch()`.
359+
* Must return a control
360+
* descriptor.
337361
*/
338362
export function* saveEntityRecord(
339363
kind,
340364
name,
341365
record,
342-
{ isAutosave = false } = { isAutosave: false }
366+
{ isAutosave = false, __unstableFetch = null } = {}
343367
) {
344368
const entities = yield getKindEntities( kind );
345369
const entity = find( entities, { kind, name } );
@@ -441,11 +465,18 @@ export function* saveEntityRecord(
441465
: data.status,
442466
}
443467
);
444-
updatedRecord = yield apiFetch( {
468+
const options = {
445469
path: `${ path }/autosaves`,
446470
method: 'POST',
447471
data,
448-
} );
472+
};
473+
if ( __unstableFetch ) {
474+
updatedRecord = yield __unstableAwaitPromise(
475+
__unstableFetch( options )
476+
);
477+
} else {
478+
updatedRecord = yield apiFetch( options );
479+
}
449480
// An autosave may be processed by the server as a regular save
450481
// when its update is requested by the author and the post had
451482
// draft or auto-draft status.
@@ -510,12 +541,18 @@ export function* saveEntityRecord(
510541
),
511542
};
512543
}
513-
514-
updatedRecord = yield apiFetch( {
544+
const options = {
515545
path,
516546
method: recordId ? 'PUT' : 'POST',
517547
data: edits,
518-
} );
548+
};
549+
if ( __unstableFetch ) {
550+
updatedRecord = yield __unstableAwaitPromise(
551+
__unstableFetch( options )
552+
);
553+
} else {
554+
updatedRecord = yield apiFetch( options );
555+
}
519556
yield receiveEntityRecords(
520557
kind,
521558
name,
@@ -543,6 +580,75 @@ export function* saveEntityRecord(
543580
}
544581
}
545582

583+
/**
584+
* Runs multiple core-data actions at the same time using one API request.
585+
*
586+
* Example:
587+
*
588+
* ```
589+
* const [ savedRecord, updatedRecord, deletedRecord ] =
590+
* await dispatch( 'core' ).__experimentalBatch( [
591+
* ( { saveEntityRecord } ) => saveEntityRecord( 'root', 'widget', widget ),
592+
* ( { saveEditedEntityRecord } ) => saveEntityRecord( 'root', 'widget', 123 ),
593+
* ( { deleteEntityRecord } ) => deleteEntityRecord( 'root', 'widget', 123, null ),
594+
* ] );
595+
* ```
596+
*
597+
* @param {Array} requests Array of functions which are invoked simultaneously.
598+
* Each function is passed an object containing
599+
* `saveEntityRecord`, `saveEditedEntityRecord`, and
600+
* `deleteEntityRecord`.
601+
*
602+
* @return {Promise} A promise that resolves to an array containing the return
603+
* values of each function given in `requests`.
604+
*/
605+
export function* __experimentalBatch( requests ) {
606+
const batch = createBatch();
607+
const dispatch = yield getDispatch();
608+
const api = {
609+
saveEntityRecord( kind, name, record, options ) {
610+
return batch.add( ( add ) =>
611+
dispatch( 'core' ).saveEntityRecord( kind, name, record, {
612+
...options,
613+
__unstableFetch: add,
614+
} )
615+
);
616+
},
617+
saveEditedEntityRecord( kind, name, recordId, options ) {
618+
return batch.add( ( add ) =>
619+
dispatch( 'core' ).saveEditedEntityRecord(
620+
kind,
621+
name,
622+
recordId,
623+
{
624+
...options,
625+
__unstableFetch: add,
626+
}
627+
)
628+
);
629+
},
630+
deleteEntityRecord( kind, name, recordId, query, options ) {
631+
return batch.add( ( add ) =>
632+
dispatch( 'core' ).deleteEntityRecord(
633+
kind,
634+
name,
635+
recordId,
636+
query,
637+
{
638+
...options,
639+
__unstableFetch: add,
640+
}
641+
)
642+
);
643+
},
644+
};
645+
const resultPromises = requests.map( ( request ) => request( api ) );
646+
const [ , ...results ] = yield __unstableAwaitPromise(
647+
Promise.all( [ batch.run(), ...resultPromises ] )
648+
);
649+
return results;
650+
}
651+
546652
/**
547653
* Action triggered to save an entity record's edits.
548654
*
@@ -571,7 +677,7 @@ export function* saveEditedEntityRecord( kind, name, recordId, options ) {
571677
recordId
572678
);
573679
const record = { id: recordId, ...edits };
574-
yield* saveEntityRecord( kind, name, record, options );
680+
return yield* saveEntityRecord( kind, name, record, options );
575681
}
576682

577683
/**

0 commit comments

Comments
 (0)