Skip to content
This repository has been archived by the owner on Jan 4, 2019. It is now read-only.

Commit

Permalink
Feat/batchactions (#52)
Browse files Browse the repository at this point in the history
* [KFI]feat(BatchActions): Add new reducer to handle batch action responses

* [KFI]feat(BatchActions): Add copy and move batch actions

* [KFI]feat(BatchActions): Modify batchActions reducers to handle general errors also

* [KFI]docs(BatchActions): Add some docs to the new reducers

* [KFI]test(BatchActions): Fix deleteBatch action tests to handle the new arguments

* [KFI]test(DeleteBatch): Fix tests

* [KFI]test(Reducers): Add tests to test batch response related reducers

* [KFI]fix(BatchActions): Add a path param to copybatch and movebatch to hold the target path

* [KFI]test(BatchActions): Add tests for testing the new batch actions

* [KFI]fix(BatchActions): Improve deleteBatch Epic

* [KFI]feat(Selection): Change select and deselect actions to handle a content except an id

* [KFI]test(Selection): Fix selection related tests

* [KFI]test(Selection): Fix selected reducer tests

* [KFI]feat(Selection): Add a new reducer to hold and handle selected content items for batch actions

* [KFI]fix(Selection): Fix selectedContentItems reducer and its tests

* [KFI]feat(Selection): Add new functions to return to value of selectedIds and selectedContentItems r

* [KFI]test(Selection): Add test for testing new selection reducers

* [KFI]feat(DeleteBatch): Change id param to contentItems

* [KFI]test(DeleteBatch): Fix deleteBatch related tests to handle content items as a param

* [KFI]feat(DeleteBatch): Complete deleteBatch functionality

* [KFI]test(DeleteBatch): Fix batch delete related tests

* [KFI]fix(BatchActions): Change copy and move batch actions first param to a contenlist object

* [KFI]test(BatchActions): Fix tests that are related to the changed param

* [KFI]feat(MoveBatch): Add move batch action to the ids and entities reducers

* [KFI]test(MoveBatch): Add moveBatch action related tests

* [KFI]feat(BatchActionEpics): Add move- and copyBatch epics

* [KFI]test(BatchEpics): Add copy- and movebatch epic tests
  • Loading branch information
herflis authored Nov 7, 2017
1 parent 9f3c050 commit fe5b11d
Show file tree
Hide file tree
Showing 6 changed files with 751 additions and 91 deletions.
88 changes: 72 additions & 16 deletions src/Actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { normalize } from 'normalizr';
import { Schemas } from './Schema';
import { Content, IContent, ODataApi, ODataHelper, Repository, ContentTypes } from 'sn-client-js';
import { Content, SavedContent, IContent, ODataApi, ContentTypes } from 'sn-client-js';

/**
* Module that contains the action creators.
Expand Down Expand Up @@ -364,25 +364,23 @@ export module Actions {
})
/**
* Action creator for deleting multiple Content from the Content Repository.
* @param path {string} Path of parent the Content.
* @param ids {string[]} Array of ids of the Content that should be deleted.
* @param ids {number[]} Array of ids of the Content that should be deleted.
* @param permanently {boolean} Defines whether Content must be moved to the Trash or deleted permanently.
* @returns {Object} Returns a redux action with the properties type, id and permanently.
*/
export const DeleteBatch = (path: string, ids: string[], permanently: boolean = false) => ({
export const DeleteBatch = (contentItems: Object, permanently: boolean = false) => ({
type: 'DELETE_BATCH_REQUEST',
path,
ids,
contentItems,
permanently
})
/**
* Action creator for the step when multiple Content deleted successfully.
* @param indexes {number[]} Array of indexes of the items in the state collection that should be removed.
* Action creator for the step when multiple Content was deleted successfully.
* @param response {ODataApi.ODataBatchResponse} response object contains the list of successes and/or errors.
* @returns {Object} Returns a redux action with the properties type and index.
*/
export const DeleteBatchSuccess = (ids: number[]) => ({
export const DeleteBatchSuccess = (response: ODataApi.ODataBatchResponse) => ({
type: 'DELETE_BATCH_SUCCESS',
ids
response
})
/**
* Action creator for the step when deleting multiple Content is failed.
Expand All @@ -393,6 +391,64 @@ export module Actions {
type: 'DELETE_BATCH_FAILURE',
message: error.message
})
/**
* Action creator for copying multiple Content in the Content Repository.
* @param ids {number[]} Array of ids of the Content that should be deleted.
* @param permanently {boolean} Defines whether Content must be moved to the Trash or deleted permanently.
* @returns {Object} Returns a redux action with the properties type, id and permanently.
*/
export const CopyBatch = (contentItems: Object, path: string) => ({
type: 'COPY_BATCH_REQUEST',
contentItems,
path
})
/**
* Action creator for the step when multiple Content was copied successfully.
* @param response {ODataApi.ODataBatchResponse} response object contains the list of successes and/or errors.
* @returns {Object} Returns a redux action with the properties type and index.
*/
export const CopyBatchSuccess = (response: ODataApi.ODataBatchResponse) => ({
type: 'COPY_BATCH_SUCCESS',
response
})
/**
* Action creator for the step when copying multiple Content is failed.
* @param error {any} The catched error object.
* @returns {Object} Returns a redux action with the properties type and the error message.
*/
export const CopyBatchFailure = (error: any) => ({
type: 'COPY_BATCH_FAILURE',
message: error.message
})
/**
* Action creator for moving multiple Content in the Content Repository.
* @param ids {number[]} Array of ids of the Content that should be deleted.
* @param permanently {boolean} Defines whether Content must be moved to the Trash or deleted permanently.
* @returns {Object} Returns a redux action with the properties type, id and permanently.
*/
export const MoveBatch = (contentItems = {}, path: string) => ({
type: 'MOVE_BATCH_REQUEST',
contentItems,
path
})
/**
* Action creator for the step when multiple Content was moved successfully.
* @param response {ODataApi.ODataBatchResponse} response object contains the list of successes and/or errors.
* @returns {Object} Returns a redux action with the properties type and index.
*/
export const MoveBatchSuccess = (response: ODataApi.ODataBatchResponse) => ({
type: 'MOVE_BATCH_SUCCESS',
response
})
/**
* Action creator for the step when moving multiple Content is failed.
* @param error {any} The catched error object.
* @returns {Object} Returns a redux action with the properties type and the error message.
*/
export const MoveBatchFailure = (error: any) => ({
type: 'MOVE_BATCH_FAILURE',
message: error.message
})
/**
* Action creator for checking out a Content in the Content Repository.
* @param content {number} Content that should be checked out.
Expand Down Expand Up @@ -666,7 +722,7 @@ export module Actions {
* @param error {any} The catched error object.
* @returns {Object} Returns a redux action with the properties type and the error message.
*/
export const UserLoginFailure = (error: {status?: number, message: string}) => ({
export const UserLoginFailure = (error: { status?: number, message: string }) => ({
type: 'USER_LOGIN_FAILURE',
message: (error.status === 403) ? 'The username or the password is not valid!' : error.message
})
Expand Down Expand Up @@ -706,20 +762,20 @@ export module Actions {
/**
* Action creator for selecting a Content
* @param id {number} The id of the selected Content
* @returns {Object} Returns a redux action.
* @returns {Object} Returns a redux action.
*/
export const SelectContent = (id) => ({
export const SelectContent = (content) => ({
type: 'SELECT_CONTENT',
id
content
})
/**
* Action creator for deselecting a Content
* @param id {number} The id of the deselected Content
* @returns {Object} Returns a redux action.
*/
export const DeSelectContent = (id) => ({
export const DeSelectContent = (content) => ({
type: 'DESELECT_CONTENT',
id
content
})/**
* Action creator for clearing the array of selected content
* @returns {Object} Returns a redux action.
Expand Down
49 changes: 43 additions & 6 deletions src/Epics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Actions } from './Actions';
import { Reducers } from './Reducers';

import { ActionsObservable, combineEpics } from 'redux-observable';
import { Observable } from 'rxjs/Observable';
import { Repository, Content, ContentTypes, Collection, ODataApi, Authentication } from 'sn-client-js';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch'

/**
* Module for redux-observable Epics of the sensenet built-in OData actions.
Expand Down Expand Up @@ -193,16 +194,50 @@ export module Epics {
export const deleteBatchEpic = (action$, store, dependencies?: { repository: Repository.BaseRepository }) => {
return action$.ofType('DELETE_BATCH_REQUEST')
.mergeMap(action => {
let collection = new Collection.Collection([], dependencies.repository, action.contentType);
return collection.Remove(action.ids, false)
let contentItems = Object.keys(action.contentItems).map(id => {
return dependencies.repository.HandleLoadedContent(action.contentItems[id], action.contentItems.__contentType);
});
return dependencies.repository.DeleteBatch(contentItems, action.permanently)
.map((response) => {
const state = store.getState();
const ids = Reducers.getIds(state.collection);
return Actions.DeleteBatchSuccess(ids);
return Actions.DeleteBatchSuccess(response);
})
.catch(error => Observable.of(Actions.DeleteBatchFailure(error)))
})
}
/**
* Epic to copy multiple Content in the Content Repository. It is related to three redux actions, returns ```CopyBatch``` action and sends the response to the
* ```CopyBatchSuccess``` action if the ajax request ended successfully or catches the error if the request failed and sends the error message to the ```CopyBatchFailure``` action.
*/
export const copyBatchEpic = (action$, store, dependencies?: { repository: Repository.BaseRepository }) => {
return action$.ofType('COPY_BATCH_REQUEST')
.mergeMap(action => {
let contentItems = Object.keys(action.contentItems).map(id => {
return dependencies.repository.HandleLoadedContent(action.contentItems[id], action.contentItems.__contentType);
});
return dependencies.repository.CopyBatch(contentItems, action.path)
.map((response) => {
return Actions.CopyBatchSuccess(response);
})
.catch(error => Observable.of(Actions.CopyBatchFailure(error)))
})
}
/**
* Epic to move multiple Content in the Content Repository. It is related to three redux actions, returns ```MoveBatch``` action and sends the response to the
* ```MoveBatchSuccess``` action if the ajax request ended successfully or catches the error if the request failed and sends the error message to the ```MoveBatchFailure``` action.
*/
export const moveBatchEpic = (action$, store, dependencies?: { repository: Repository.BaseRepository }) => {
return action$.ofType('MOVE_BATCH_REQUEST')
.mergeMap(action => {
let contentItems = Object.keys(action.contentItems).map(id => {
return dependencies.repository.HandleLoadedContent(action.contentItems[id], action.contentItems.__contentType);
});
return dependencies.repository.MoveBatch(contentItems, action.path)
.map((response) => {
return Actions.MoveBatchSuccess(response);
})
.catch(error => Observable.of(Actions.MoveBatchFailure(error)))
})
}
/**
* Epic to checkout a Content in the Content Repository. It is related to three redux actions, returns ```CheckOut``` action and sends the response to the
* ```CheckOutSuccess``` action if the ajax request ended successfully or catches the error if the request failed and sends the error message to the ```CheckOutFailure``` action.
Expand Down Expand Up @@ -402,6 +437,8 @@ export module Epics {
updateContentEpic,
deleteContentEpic,
deleteBatchEpic,
copyBatchEpic,
moveBatchEpic,
checkoutContentEpic,
checkinContentEpic,
publishContentEpic,
Expand Down
101 changes: 92 additions & 9 deletions src/Reducers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { normalize } from 'normalizr';
import { combineReducers } from 'redux';
import { Authentication } from 'sn-client-js';

Expand Down Expand Up @@ -188,6 +187,18 @@ export module Reducers {
return state
case 'DELETE_CONTENT_SUCCESS':
return [...state.slice(0, action.index), ...state.slice(action.index + 1)]
case 'DELETE_BATCH_SUCCESS':
case 'MOVE_BATCH_SUCCESS':
if (action.response.d.results.length > 0) {
let newIds = []
let deletedIds = action.response.d.results.map(result => result.Id)
for (let i = 0; i < state.length; i++) {
if (deletedIds.indexOf(state[i]) === -1) {
newIds.push(state[i])
}
}
return newIds
}
default:
return state;
}
Expand All @@ -205,14 +216,22 @@ export module Reducers {
action.type !== 'LOAD_CONTENT_SUCCESS' &&
action.type !== 'REQUEST_CONTENT_ACTIONS_SUCCESS' &&
action.type !== 'UPDATE_CONTENT_SUCCESS' &&
action.type !== 'UPLOAD_CONTENT_SUCCESS')) {
action.type !== 'UPLOAD_CONTENT_SUCCESS' &&
action.type !== 'DELETE_BATCH_SUCCESS' &&
action.type !== 'COPY_BATCH_SUCCESS' &&
action.type !== 'MOVE_BATCH_SUCCESS')) {
return (<any>Object).assign({}, state, action.response.entities.entities);
}
switch (action.type) {
case 'DELETE_CONTENT_SUCCESS':
let res = Object.assign({}, state);
delete res[action.id];
return res;
case 'DELETE_BATCH_SUCCESS':
case 'MOVE_BATCH_SUCCESS':
let resource = Object.assign({}, state);
action.response.d.results.map(result => delete resource[result.Id])
return resource;
case 'UPDATE_CONTENT_SUCCESS':
state[action.response.Id] = action.response
return state
Expand Down Expand Up @@ -584,31 +603,91 @@ export module Reducers {
})
/**
* Reducer to handle Actions on the selected array.
* @param {Object} [state=[]] Represents the current state.
* @param {Array} [state=[]] Represents the current state.
* @param {Object} action Represents an action that is called.
* @returns {Object} state. Returns the next state based on the action.
*/
export const selected = (state = [], action) => {
export const selectedIds = (state = [], action) => {
switch (action.type) {
case 'SELECT_CONTENT':
return [...state, action.id]
return [...state, action.content.Id]
case 'DESELECT_CONTENT':
const index = state.indexOf(action.id)
const index = state.indexOf(action.content.Id)
return [...state.slice(0, index), ...state.slice(index + 1)]
case 'CLEAR_SELECTION':
return []
default:
return state
}
}
export const selectedContentItems = (state = {}, action) => {
switch (action.type) {
case 'DESELECT_CONTENT':
let res = Object.assign({}, state);
delete res[action.content.Id];
return res;
case 'SELECT_CONTENT':
let obj = {}
obj[action.content.Id] = action.content
return (<any>Object).assign({}, state, obj);
case 'CLEAR_SELECTION':
return {}
default:
return state;
}
}
export const selected = combineReducers({
ids: selectedIds,
entities: selectedContentItems
})
/**
* Reducer to handle Actions on the OdataBatchResponse Object.
* @param {Array} state Represents the current state.
* @param {Object} action Represents an action that is called.
* @returns {Object} state. Returns the next state based on the action.
*/
export const OdataBatchResponse = (state = Object, action) => {
switch (action.type) {
case 'DELETE_BATCH_SUCCESS':
case 'COPY_BATCH_SUCCESS':
case 'MOVE_BATCH_SUCCESS':
return action.response
default:
return {}
}
}
/**
* Reducer to handle Actions on the batchResponseError Object.
* @param {string} state Represents the current state.
* @param {Object} action Represents an action that is called.
* @returns {Object} state. Returns the next state based on the action.
*/
export const batchResponseError = (state = '', action) => {
switch (action.type) {
case 'DELETE_BATCH_FAILURE':
case 'COPY_BATCH_FAILURE':
case 'MOVE_BATCH_FAILURE':
return action.message
default:
return ''
}
}
/**
* Reducer combining response and error into a single object, ```batchResponses```.
*/
export const batchResponses = combineReducers({
response: OdataBatchResponse,
error: batchResponseError
})
/**
* Reducer combining session, children, currentcontent and selected into a single object, ```sensenet``` which will be the top-level one.
*/
export const sensenet = combineReducers({
session,
children,
currentcontent,
selected
selected,
batchResponses
})

/**
Expand Down Expand Up @@ -651,8 +730,12 @@ export module Reducers {
return state.session.repository.RepositoryUrl;
}

export const getSelectedContent = (state) => {
return state.selected
export const getSelectedContentIds = (state) => {
return state.selected.ids
}

export const getSelectedContentItems = (state) => {
return state.selected.entities
}

export const getOpenedContent = (state) => {
Expand Down
Loading

0 comments on commit fe5b11d

Please sign in to comment.