Skip to content

Commit

Permalink
[ CoreData Entities ] add support for delete operations (#21557)
Browse files Browse the repository at this point in the history
* reset, delete nav menu items

there is a missing state preservation when menus are changed on the menus editor component

* fixing a bad merge

* no invalidateCache and refactored according to review

* revert change to getMergedItemIds

* refactor according to self review

* deletes query items, adds tests for new entity methods

also adds changelog

* refactored for properly deleteing entity ids from querries

* updates tests

* moved the REMOVE_ITEMS reducer to receive all query keys

props @aduth

* removes the need to send query on delete

* updates some comments

also removes cruft from a test

* refactoring according to review

- removes superfluous actions from deleteEntityRecords
- treats invalidateCache properly
- attempts to make lookups faster for items when removing querries

* do not clear cache on delete

* fixes test after reming superfluous actions from deleteEntityRecord

* makes a proper POJO for removing items from queries and invalidates the cache after items are removed

* fix changelog and add deleteQueryParams to deleteRecord

* fixes test

* Rename deleteQueryParams to query for consistency

* Use the new deleteEntityRecord to delete menus (#22428)

* adds delete menu with entity delete

* updates the delete and removes the stateMenus

* passes the new force query param

* fix bug with resetting current menu after delete

* fixes query param's type for consistency

* comment linting, removed useless catch logic for deleteEntityRecord, removed useless string conversion

* try implement error handling for delete

* fixed the intentional typo and the unintentional one

* updates and fixes according to review

* rename remove items' action id collection

* makes notices unique in menu editor

* Update packages/core-data/src/queried-data/actions.js

Co-authored-by: Enrique Piqueras <epiqueras@users.noreply.github.com>

* fixes bugs introduced by renaming items to itemsIds in the remove items action

also improves tests and made sure the notifications always have unique ids

* lint

* moves noticeId creation inside effect

* updated according to review

- test delete for final shape when generator is done
- simplify filter removing items from query

* lint

Co-authored-by: Enrique Piqueras <epiqueras@users.noreply.github.com>
  • Loading branch information
draganescu and epiqueras committed Jul 28, 2020
1 parent 83abb17 commit b67cd9a
Show file tree
Hide file tree
Showing 15 changed files with 408 additions and 45 deletions.
41 changes: 41 additions & 0 deletions docs/designers-developers/developers/data/data-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,21 @@ _Returns_

- `?Array`: Records.

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

Returns the specified entity record's last delete error.

_Parameters_

- _state_ `Object`: State tree.
- _kind_ `string`: Entity kind.
- _name_ `string`: Entity name.
- _recordId_ `number`: Record ID.

_Returns_

- `?Object`: The entity record's save error.

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

Returns the specified entity record's last save error.
Expand Down Expand Up @@ -407,6 +422,21 @@ _Returns_

- `boolean`: Whether the entity record is autosaving or not.

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

Returns true if the specified entity record is deleting, and false otherwise.

_Parameters_

- _state_ `Object`: State tree.
- _kind_ `string`: Entity kind.
- _name_ `string`: Entity name.
- _recordId_ `number`: Record ID.

_Returns_

- `boolean`: Whether the entity record is deleting or not.

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

Determines if the returned preview is an oEmbed link fallback.
Expand Down Expand Up @@ -471,6 +501,17 @@ _Returns_

- `Object`: Action object.

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

Action triggered to delete an entity record.

_Parameters_

- _kind_ `string`: Kind of the deleted entity.
- _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.

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

Returns an action object that triggers an
Expand Down
6 changes: 6 additions & 0 deletions packages/core-data/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

### New Feature

- The `deleteEntityRecord` and `removeItems` actions have been added.
- The `isDeletingEntityRecord` and `getLastEntityDeleteError` selectors have been added.
- A `delete<entity.name>` helper is created for every registered entity.

## 2.3.0 (2019-05-21)

### New features
Expand Down
41 changes: 41 additions & 0 deletions packages/core-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ _Returns_

- `Object`: Action object.

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

Action triggered to delete an entity record.

_Parameters_

- _kind_ `string`: Kind of the deleted entity.
- _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.

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

Returns an action object that triggers an
Expand Down Expand Up @@ -440,6 +451,21 @@ _Returns_

- `?Array`: Records.

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

Returns the specified entity record's last delete error.

_Parameters_

- _state_ `Object`: State tree.
- _kind_ `string`: Entity kind.
- _name_ `string`: Entity name.
- _recordId_ `number`: Record ID.

_Returns_

- `?Object`: The entity record's save error.

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

Returns the specified entity record's last save error.
Expand Down Expand Up @@ -633,6 +659,21 @@ _Returns_

- `boolean`: Whether the entity record is autosaving or not.

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

Returns true if the specified entity record is deleting, and false otherwise.

_Parameters_

- _state_ `Object`: State tree.
- _kind_ `string`: Entity kind.
- _name_ `string`: Entity name.
- _recordId_ `number`: Record ID.

_Returns_

- `boolean`: Whether the entity record is deleting or not.

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

Determines if the returned preview is an oEmbed link fallback.
Expand Down
59 changes: 58 additions & 1 deletion packages/core-data/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
*/
import { castArray, get, isEqual, find } from 'lodash';

/**
* WordPress dependencies
*/
import { addQueryArgs } from '@wordpress/url';

/**
* Internal dependencies
*/
import { receiveItems, receiveQueriedItems } from './queried-data';
import { receiveItems, removeItems, receiveQueriedItems } from './queried-data';
import { getKindEntities, DEFAULT_ENTITY_KEY } from './entities';
import { select, apiFetch } from './controls';

Expand Down Expand Up @@ -139,6 +144,58 @@ 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.
*/
export function* deleteEntityRecord( kind, name, recordId, query ) {
const entities = yield getKindEntities( kind );
const entity = find( entities, { kind, name } );
let error;
let deletedRecord = false;
if ( ! entity ) {
return;
}

yield {
type: 'DELETE_ENTITY_RECORD_START',
kind,
name,
recordId,
};

try {
let path = `${ entity.baseURL }/${ recordId }`;

if ( query ) {
path = addQueryArgs( path, query );
}

deletedRecord = yield apiFetch( {
path,
method: 'DELETE',
} );

yield removeItems( kind, name, recordId, true );
} catch ( _error ) {
error = _error;
}

yield {
type: 'DELETE_ENTITY_RECORD_FINISH',
kind,
name,
recordId,
error,
};

return deletedRecord;
}

/**
* Returns an action object that triggers an
* edit to an entity record.
Expand Down
2 changes: 2 additions & 0 deletions packages/core-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const entityActions = defaultEntities.reduce( ( result, entity ) => {
const { kind, name } = entity;
result[ getMethodName( kind, name, 'save' ) ] = ( key ) =>
actions.saveEntityRecord( kind, name, key );
result[ getMethodName( kind, name, 'delete' ) ] = ( key, query ) =>
actions.deleteEntityRecord( kind, name, key, query );
return result;
}, {} );

Expand Down
20 changes: 20 additions & 0 deletions packages/core-data/src/queried-data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ export function receiveItems( items ) {
};
}

/**
* Returns an action object used in signalling that entity records have been
* deleted and they need to be removed from entities state.
*
* @param {string} kind Kind of the removed entities.
* @param {string} name Name of the removed entities.
* @param {Array|number} records Record IDs of the removed entities.
* @param {boolean} invalidateCache Controls whether we want to invalidate the cache.
* @return {Object} Action object.
*/
export function removeItems( kind, name, records, invalidateCache = false ) {
return {
type: 'REMOVE_ITEMS',
itemIds: castArray( records ),
kind,
name,
invalidateCache,
};
}

/**
* Returns an action object used in signalling that queried data has been
* received.
Expand Down
37 changes: 34 additions & 3 deletions packages/core-data/src/queried-data/reducer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { map, flowRight } from 'lodash';
import { map, flowRight, omit, forEach, filter } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -86,8 +86,10 @@ function items( state = {}, action ) {
return accumulator;
}, {} ),
};
case 'REMOVE_ITEMS':
const newState = omit( state, action.itemIds );
return newState;
}

return state;
}

Expand All @@ -100,7 +102,7 @@ function items( state = {}, action ) {
*
* @return {Object} Next state.
*/
const queries = flowRight( [
const receiveQueries = flowRight( [
// Limit to matching action type so we don't attempt to replace action on
// an unhandled action.
ifMatchingAction( ( action ) => 'query' in action ),
Expand Down Expand Up @@ -138,6 +140,35 @@ const queries = flowRight( [
);
} );

/**
* Reducer tracking queries state.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Next state.
*/
const queries = ( state = {}, action ) => {
switch ( action.type ) {
case 'RECEIVE_ITEMS':
return receiveQueries( state, action );
case 'REMOVE_ITEMS':
const newState = { ...state };
const removedItems = action.itemIds.reduce( ( result, itemId ) => {
result[ itemId ] = true;
return result;
}, {} );
forEach( newState, ( queryItems, key ) => {
newState[ key ] = filter( queryItems, ( queryId ) => {
return ! removedItems[ queryId ];
} );
} );
return newState;
default:
return state;
}
};

export default combineReducers( {
items,
queries,
Expand Down
17 changes: 17 additions & 0 deletions packages/core-data/src/queried-data/test/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Internal dependencies
*/
import { removeItems } from '../actions';

describe( 'removeItems', () => {
it( 'builds an action object', () => {
const postIds = [ 1, 2, 3 ];
expect( removeItems( 'postType', 'post', postIds ) ).toEqual( {
type: 'REMOVE_ITEMS',
itemIds: postIds,
kind: 'postType',
name: 'post',
invalidateCache: false,
} );
} );
} );
31 changes: 31 additions & 0 deletions packages/core-data/src/queried-data/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import deepFreeze from 'deep-freeze';
* Internal dependencies
*/
import reducer, { getMergedItemIds } from '../reducer';
import { removeItems } from '../actions';

describe( 'getMergedItemIds', () => {
it( 'should receive a page', () => {
Expand Down Expand Up @@ -113,4 +114,34 @@ describe( 'reducer', () => {
queries: {},
} );
} );

it( 'deletes an item', () => {
const kind = 'root';
const name = 'menu';
const original = deepFreeze( {
items: {
1: { id: 1, name: 'abc' },
2: { id: 2, name: 'def' },
3: { id: 3, name: 'ghi' },
4: { id: 4, name: 'klm' },
},
queries: {
'': [ 1, 2, 3, 4 ],
's=a': [ 1, 3 ],
},
} );
const state = reducer( original, removeItems( kind, name, 3 ) );

expect( state ).toEqual( {
items: {
1: { id: 1, name: 'abc' },
2: { id: 2, name: 'def' },
4: { id: 4, name: 'klm' },
},
queries: {
'': [ 1, 2, 4 ],
's=a': [ 1 ],
},
} );
} );
} );
Loading

0 comments on commit b67cd9a

Please sign in to comment.