Skip to content

Commit

Permalink
Replace batch processing store with a simpler API (#28210)
Browse files Browse the repository at this point in the history
- 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 ).
  • Loading branch information
noisysocks authored Jan 28, 2021
1 parent b0d4e1f commit 12d81cd
Show file tree
Hide file tree
Showing 20 changed files with 714 additions and 855 deletions.
3 changes: 3 additions & 0 deletions docs/designers-developers/developers/data/data-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,8 @@ _Parameters_
- _name_ `string`: Name of the deleted entity.
- _recordId_ `string`: Record ID of the deleted entity.
- _query_ `?Object`: Special query parameters for the DELETE API call.
- _options_ `[Object]`: Delete options.
- _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a control descriptor.

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

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

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

Expand Down
3 changes: 3 additions & 0 deletions packages/core-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ _Parameters_
- _name_ `string`: Name of the deleted entity.
- _recordId_ `string`: Record ID of the deleted entity.
- _query_ `?Object`: Special query parameters for the DELETE API call.
- _options_ `[Object]`: Delete options.
- _options.\_\_unstableFetch_ `[Function]`: Internal use only. Function to call instead of `apiFetch()`. Must return a control descriptor.

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

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

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

Expand Down
146 changes: 126 additions & 20 deletions packages/core-data/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { v4 as uuid } from 'uuid';
* WordPress dependencies
*/
import { controls } from '@wordpress/data';
import { apiFetch } from '@wordpress/data-controls';
import { apiFetch, __unstableAwaitPromise } from '@wordpress/data-controls';
import { addQueryArgs } from '@wordpress/url';

/**
Expand All @@ -20,6 +20,8 @@ import {
__unstableAcquireStoreLock,
__unstableReleaseStoreLock,
} from './locks';
import { createBatch } from './batch';
import { getDispatch } from './controls';

/**
* Returns an action object used in signalling that authors have been received.
Expand Down Expand Up @@ -154,12 +156,23 @@ export function receiveEmbedPreview( url, preview ) {
/**
* Action triggered to delete an entity record.
*
* @param {string} kind Kind of the deleted entity.
* @param {string} name Name of the deleted entity.
* @param {string} recordId Record ID of the deleted entity.
* @param {?Object} query Special query parameters for the DELETE API call.
* @param {string} kind Kind of the deleted entity.
* @param {string} name Name of the deleted entity.
* @param {string} recordId Record ID of the deleted entity.
* @param {?Object} query Special query parameters for the
* DELETE API call.
* @param {Object} [options] Delete options.
* @param {Function} [options.__unstableFetch] Internal use only. Function to
* call instead of `apiFetch()`.
* Must return a control descriptor.
*/
export function* deleteEntityRecord( kind, name, recordId, query ) {
export function* deleteEntityRecord(
kind,
name,
recordId,
query,
{ __unstableFetch = null } = {}
) {
const entities = yield getKindEntities( kind );
const entity = find( entities, { kind, name } );
let error;
Expand Down Expand Up @@ -188,10 +201,17 @@ export function* deleteEntityRecord( kind, name, recordId, query ) {
path = addQueryArgs( path, query );
}

deletedRecord = yield apiFetch( {
const options = {
path,
method: 'DELETE',
} );
};
if ( __unstableFetch ) {
deletedRecord = yield __unstableAwaitPromise(
__unstableFetch( options )
);
} else {
deletedRecord = yield apiFetch( options );
}

yield removeItems( kind, name, recordId, true );
} catch ( _error ) {
Expand Down Expand Up @@ -329,17 +349,21 @@ export function __unstableCreateUndoLevel() {
/**
* Action triggered to save an entity record.
*
* @param {string} kind Kind of the received entity.
* @param {string} name Name of the received entity.
* @param {Object} record Record to be saved.
* @param {Object} options Saving options.
* @param {boolean} [options.isAutosave=false] Whether this is an autosave.
* @param {string} kind Kind of the received entity.
* @param {string} name Name of the received entity.
* @param {Object} record Record to be saved.
* @param {Object} options Saving options.
* @param {boolean} [options.isAutosave=false] Whether this is an autosave.
* @param {Function} [options.__unstableFetch] Internal use only. Function to
* call instead of `apiFetch()`.
* Must return a control
* descriptor.
*/
export function* saveEntityRecord(
kind,
name,
record,
{ isAutosave = false } = { isAutosave: false }
{ isAutosave = false, __unstableFetch = null } = {}
) {
const entities = yield getKindEntities( kind );
const entity = find( entities, { kind, name } );
Expand Down Expand Up @@ -441,11 +465,18 @@ export function* saveEntityRecord(
: data.status,
}
);
updatedRecord = yield apiFetch( {
const options = {
path: `${ path }/autosaves`,
method: 'POST',
data,
} );
};
if ( __unstableFetch ) {
updatedRecord = yield __unstableAwaitPromise(
__unstableFetch( options )
);
} else {
updatedRecord = yield apiFetch( options );
}
// An autosave may be processed by the server as a regular save
// when its update is requested by the author and the post had
// draft or auto-draft status.
Expand Down Expand Up @@ -510,12 +541,18 @@ export function* saveEntityRecord(
),
};
}

updatedRecord = yield apiFetch( {
const options = {
path,
method: recordId ? 'PUT' : 'POST',
data: edits,
} );
};
if ( __unstableFetch ) {
updatedRecord = yield __unstableAwaitPromise(
__unstableFetch( options )
);
} else {
updatedRecord = yield apiFetch( options );
}
yield receiveEntityRecords(
kind,
name,
Expand Down Expand Up @@ -543,6 +580,75 @@ export function* saveEntityRecord(
}
}

/**
* Runs multiple core-data actions at the same time using one API request.
*
* Example:
*
* ```
* const [ savedRecord, updatedRecord, deletedRecord ] =
* await dispatch( 'core' ).__experimentalBatch( [
* ( { saveEntityRecord } ) => saveEntityRecord( 'root', 'widget', widget ),
* ( { saveEditedEntityRecord } ) => saveEntityRecord( 'root', 'widget', 123 ),
* ( { deleteEntityRecord } ) => deleteEntityRecord( 'root', 'widget', 123, null ),
* ] );
* ```
*
* @param {Array} requests Array of functions which are invoked simultaneously.
* Each function is passed an object containing
* `saveEntityRecord`, `saveEditedEntityRecord`, and
* `deleteEntityRecord`.
*
* @return {Promise} A promise that resolves to an array containing the return
* values of each function given in `requests`.
*/
export function* __experimentalBatch( requests ) {
const batch = createBatch();
const dispatch = yield getDispatch();
const api = {
saveEntityRecord( kind, name, record, options ) {
return batch.add( ( add ) =>
dispatch( 'core' ).saveEntityRecord( kind, name, record, {
...options,
__unstableFetch: add,
} )
);
},
saveEditedEntityRecord( kind, name, recordId, options ) {
return batch.add( ( add ) =>
dispatch( 'core' ).saveEditedEntityRecord(
kind,
name,
recordId,
{
...options,
__unstableFetch: add,
}
)
);
},
deleteEntityRecord( kind, name, recordId, query, options ) {
return batch.add( ( add ) =>
dispatch( 'core' ).deleteEntityRecord(
kind,
name,
recordId,
query,
{
...options,
__unstableFetch: add,
}
)
);
},
};
const resultPromises = requests.map( ( request ) => request( api ) );
const [ , ...results ] = yield __unstableAwaitPromise(
Promise.all( [ batch.run(), ...resultPromises ] )
);
return results;
}

/**
* Action triggered to save an entity record's edits.
*
Expand Down Expand Up @@ -571,7 +677,7 @@ export function* saveEditedEntityRecord( kind, name, recordId, options ) {
recordId
);
const record = { id: recordId, ...edits };
yield* saveEntityRecord( kind, name, record, options );
return yield* saveEntityRecord( kind, name, record, options );
}

/**
Expand Down
Loading

0 comments on commit 12d81cd

Please sign in to comment.