From b242245e83b5394e3daab44511caa9f35bd3b05e Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 15:25:36 +0100 Subject: [PATCH 01/27] [KFI]feat(BatchActions): Add new reducer to handle batch action responses --- src/Reducers.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index 18d024e..6e1d794 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -601,6 +601,16 @@ export module Reducers { return state } } + export const batchResponses = (state = Object, action) => { + switch (action.type) { + case 'DELETE_BATCH_SUCCESS': + case 'COPY_BATCH_SUCCESS': + case 'MOVE_BATCH_SUCCESS': + return action.reponse + default: + return {} + } + } /** * Reducer combining session, children, currentcontent and selected into a single object, ```sensenet``` which will be the top-level one. */ @@ -608,7 +618,8 @@ export module Reducers { session, children, currentcontent, - selected + selected, + batchResponses }) /** From f8f28a3953fc29df80b5a2264ce38a1f79148254 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 15:28:54 +0100 Subject: [PATCH 02/27] [KFI]feat(BatchActions): Add copy and move batch actions --- src/Actions.ts | 72 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/src/Actions.ts b/src/Actions.ts index 9461fc6..7249404 100644 --- a/src/Actions.ts +++ b/src/Actions.ts @@ -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, IContent, ODataApi, ContentTypes} from 'sn-client-js'; /** * Module that contains the action creators. @@ -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 = (ids: number[], permanently: boolean = false) => ({ type: 'DELETE_BATCH_REQUEST', - path, ids, 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. @@ -393,6 +391,62 @@ 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 = (ids: number[], permanently: boolean = false) => ({ + type: 'COPY_BATCH_REQUEST', + ids + }) + /** + * 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 = (ids: number[], permanently: boolean = false) => ({ + type: 'MOVE_BATCH_REQUEST', + ids + }) + /** + * 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. From 02325a186c447c50ebd44dccf1ab47c12d99c8ee Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 15:33:26 +0100 Subject: [PATCH 03/27] [KFI]feat(BatchActions): Modify batchActions reducers to handle general errors also --- src/Reducers.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index 6e1d794..e5c693c 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -601,7 +601,7 @@ export module Reducers { return state } } - export const batchResponses = (state = Object, action) => { + export const OdataBatchResponse = (state = Object, action) => { switch (action.type) { case 'DELETE_BATCH_SUCCESS': case 'COPY_BATCH_SUCCESS': @@ -611,6 +611,20 @@ export module Reducers { return {} } } + 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 '' + } + } + 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. */ From 34e0973f0a010eb65a2a8fb89f80fb9963c9f941 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 15:41:57 +0100 Subject: [PATCH 04/27] [KFI]docs(BatchActions): Add some docs to the new reducers --- src/Reducers.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index e5c693c..d2f7eb2 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -584,7 +584,7 @@ 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. */ @@ -601,6 +601,12 @@ export module Reducers { return state } } + /** + * 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': @@ -611,6 +617,12 @@ export module Reducers { 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': @@ -621,6 +633,9 @@ export module Reducers { return '' } } + /** + * Reducer combining response and error into a single object, ```batchResponses```. + */ export const batchResponses = combineReducers({ response: OdataBatchResponse, error: batchResponseError From 6e22c4e9a655414358a362c372651c3749aae718 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 15:48:39 +0100 Subject: [PATCH 05/27] [KFI]test(BatchActions): Fix deleteBatch action tests to handle the new arguments --- test/ActionsTests.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index a6a41b8..662755a 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -1,7 +1,7 @@ /// import { Actions } from '../src/Actions' import * as Chai from 'chai'; -import { Mocks, ContentTypes, Repository } from 'sn-client-js'; +import { Mocks, ContentTypes, Repository, ODataApi } from 'sn-client-js'; const expect = Chai.expect; describe('Actions', () => { @@ -252,7 +252,7 @@ describe('Actions', () => { ids: ['1', '2', '3'], permanently: false } - expect(Actions.DeleteBatch(path, ['1', '2', '3'], false)).to.deep.equal(expectedAction) + expect(Actions.DeleteBatch([1, 2, 3], false)).to.deep.equal(expectedAction) }); it('should create an action to a delete content request', () => { const expectedAction = { @@ -261,14 +261,15 @@ describe('Actions', () => { ids: ['1', '2', '3'], permanently: false } - expect(Actions.DeleteBatch(path, ['1', '2', '3'])).to.deep.equal(expectedAction) + expect(Actions.DeleteBatch([1, 2, 3])).to.deep.equal(expectedAction) }); it('should create an action to delete content success', () => { + const response = new ODataApi.ODataBatchResponse() const expectedAction = { type: 'DELETE_BATCH_SUCCESS', - ids: [0, 1, 2] + response: response } - expect(Actions.DeleteBatchSuccess([0, 1, 2])).to.deep.equal(expectedAction) + expect(Actions.DeleteBatchSuccess(response)).to.deep.equal(expectedAction) }); it('should create an action to delete content failure', () => { const expectedAction = { From 6e2e684100ec2688b37b4351e402dbe1f986b1d7 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 16:03:13 +0100 Subject: [PATCH 06/27] [KFI]test(DeleteBatch): Fix tests --- test/ActionsTests.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index 662755a..5803f55 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -248,8 +248,7 @@ describe('Actions', () => { it('should create an action to a delete content request', () => { const expectedAction = { type: 'DELETE_BATCH_REQUEST', - path: path, - ids: ['1', '2', '3'], + ids: [1, 2, 3], permanently: false } expect(Actions.DeleteBatch([1, 2, 3], false)).to.deep.equal(expectedAction) @@ -257,8 +256,7 @@ describe('Actions', () => { it('should create an action to a delete content request', () => { const expectedAction = { type: 'DELETE_BATCH_REQUEST', - path: path, - ids: ['1', '2', '3'], + ids: [1, 2, 3], permanently: false } expect(Actions.DeleteBatch([1, 2, 3])).to.deep.equal(expectedAction) From 670ac5bbc82dc32e33a702cd5444d3372a94ddc7 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 16:14:15 +0100 Subject: [PATCH 07/27] [KFI]test(Reducers): Add tests to test batch response related reducers --- src/Reducers.ts | 2 +- test/ReducersTests.ts | 78 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index d2f7eb2..84a8637 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -612,7 +612,7 @@ export module Reducers { case 'DELETE_BATCH_SUCCESS': case 'COPY_BATCH_SUCCESS': case 'MOVE_BATCH_SUCCESS': - return action.reponse + return action.response default: return {} } diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index 7da2a46..274fb50 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -787,6 +787,84 @@ describe('Reducers', () => { expect(Reducers.selected([1], action)).to.deep.equal([]); }) }) + describe('batchResponseError reducer', () => { + it('should return the initial state', () => { + expect(Reducers.batchResponseError(undefined, {})).to.deep.equal(''); + }); + it('should return an error message', () => { + const action = { + type: 'DELETE_BATCH_FAILURE', + message: 'error' + } + expect(Reducers.batchResponseError(undefined, action)).to.deep.equal('error'); + }) + it('should return an error message', () => { + const action = { + type: 'COPY_BATCH_FAILURE', + message: 'error' + } + expect(Reducers.batchResponseError(undefined, action)).to.deep.equal('error'); + }) + it('should return an error message', () => { + const action = { + type: 'MOVE_BATCH_FAILURE', + message: 'error' + } + expect(Reducers.batchResponseError(undefined, action)).to.deep.equal('error'); + }) + it('should return an empty string', () => { + const action = { + type: 'MOVE_BATCH_SUCCESS', + response: {} + } + expect(Reducers.batchResponseError(undefined, action)).to.deep.equal(''); + }) + }) + describe('OdataBatchResponse reducer', () => { + it('should return the initial state', () => { + expect(Reducers.OdataBatchResponse(undefined, {})).to.deep.equal({}); + }); + it('should return a response object', () => { + const action = { + type: 'DELETE_BATCH_SUCCESS', + response: { + vmi: '1' + } + } + expect(Reducers.OdataBatchResponse(undefined, action)).to.deep.equal({ + vmi: '1' + }); + }) + it('should return an error message', () => { + const action = { + type: 'COPY_BATCH_SUCCESS', + response: { + vmi: '1' + } + } + expect(Reducers.OdataBatchResponse(undefined, action)).to.deep.equal({ + vmi: '1' + }); + }) + it('should return an error message', () => { + const action = { + type: 'MOVE_BATCH_SUCCESS', + response: { + vmi: '1' + } + } + expect(Reducers.OdataBatchResponse(undefined, action)).to.deep.equal({ + vmi: '1' + }); + }) + it('should return an empty string', () => { + const action = { + type: 'MOVE_BATCH_FAILURE', + message: 'error' + } + expect(Reducers.OdataBatchResponse(undefined, action)).to.deep.equal({}); + }) + }) describe('getContent', () => { const state = { entities: { From 325cd05dc7cd577c574543904a72614921a19ae9 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 16:16:55 +0100 Subject: [PATCH 08/27] [KFI]fix(BatchActions): Add a path param to copybatch and movebatch to hold the target path --- src/Actions.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Actions.ts b/src/Actions.ts index 7249404..9b110b3 100644 --- a/src/Actions.ts +++ b/src/Actions.ts @@ -397,9 +397,10 @@ export module Actions { * @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 = (ids: number[], permanently: boolean = false) => ({ + export const CopyBatch = (ids: number[], path) => ({ type: 'COPY_BATCH_REQUEST', - ids + ids, + path }) /** * Action creator for the step when multiple Content was copied successfully. @@ -425,9 +426,10 @@ export module Actions { * @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 = (ids: number[], permanently: boolean = false) => ({ + export const MoveBatch = (ids: number[], path) => ({ type: 'MOVE_BATCH_REQUEST', - ids + ids, + path }) /** * Action creator for the step when multiple Content was moved successfully. From cbf45a3d4f1883e04242959561bae69df9f1b972 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 16:21:43 +0100 Subject: [PATCH 09/27] [KFI]test(BatchActions): Add tests for testing the new batch actions --- test/ActionsTests.ts | 58 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index 5803f55..d2c0942 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -245,14 +245,6 @@ describe('Actions', () => { }); }); describe('DeleteBatchContent', () => { - it('should create an action to a delete content request', () => { - const expectedAction = { - type: 'DELETE_BATCH_REQUEST', - ids: [1, 2, 3], - permanently: false - } - expect(Actions.DeleteBatch([1, 2, 3], false)).to.deep.equal(expectedAction) - }); it('should create an action to a delete content request', () => { const expectedAction = { type: 'DELETE_BATCH_REQUEST', @@ -277,6 +269,56 @@ describe('Actions', () => { expect(Actions.DeleteBatchFailure({ message: 'error' })).to.deep.equal(expectedAction) }); }); + describe('CopyBatchContent', () => { + it('should create an action to a copy multiple content request', () => { + const expectedAction = { + type: 'COPY_BATCH_REQUEST', + ids: [1, 2, 3], + path: '/workspaces' + } + expect(Actions.CopyBatch([1, 2, 3], '/workspaces')).to.deep.equal(expectedAction) + }); + it('should create an action to copy multiple content success', () => { + const response = new ODataApi.ODataBatchResponse() + const expectedAction = { + type: 'COPY_BATCH_SUCCESS', + response: response + } + expect(Actions.CopyBatchSuccess(response)).to.deep.equal(expectedAction) + }); + it('should create an action to copy multiple content failure', () => { + const expectedAction = { + type: 'COPY_BATCH_FAILURE', + message: 'error' + } + expect(Actions.CopyBatchFailure({ message: 'error' })).to.deep.equal(expectedAction) + }); + }); + describe('MoveBatchContent', () => { + it('should create an action to a move multiple content request', () => { + const expectedAction = { + type: 'MOVE_BATCH_REQUEST', + ids: [1, 2, 3], + path: '/workspaces' + } + expect(Actions.MoveBatch([1, 2, 3], '/workspaces')).to.deep.equal(expectedAction) + }); + it('should create an action to move multiple content success', () => { + const response = new ODataApi.ODataBatchResponse() + const expectedAction = { + type: 'MOVE_BATCH_SUCCESS', + response: response + } + expect(Actions.MoveBatchSuccess(response)).to.deep.equal(expectedAction) + }); + it('should create an action to move multiple content failure', () => { + const expectedAction = { + type: 'MOVE_BATCH_FAILURE', + message: 'error' + } + expect(Actions.MoveBatchFailure({ message: 'error' })).to.deep.equal(expectedAction) + }); + }); describe('CheckoutContent', () => { const content = repo.CreateContent({ DisplayName: 'My content', Id: 123 }, ContentTypes.Task) it('should create an action to a checkout content request', () => { From de8a8d1292a162509d8a64be94f67b028c0d8f91 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Mon, 6 Nov 2017 16:49:50 +0100 Subject: [PATCH 10/27] [KFI]fix(BatchActions): Improve deleteBatch Epic --- src/Epics.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Epics.ts b/src/Epics.ts index e6571b8..4a1d7d5 100644 --- a/src/Epics.ts +++ b/src/Epics.ts @@ -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. @@ -194,11 +195,9 @@ export module Epics { return action$.ofType('DELETE_BATCH_REQUEST') .mergeMap(action => { let collection = new Collection.Collection([], dependencies.repository, action.contentType); - return collection.Remove(action.ids, false) + return collection.Remove(action.ids, 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))) }) From d42354b4484c0f89b8e8d0dc547553b8c89cb7cc Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:29:00 +0100 Subject: [PATCH 11/27] [KFI]feat(Selection): Change select and deselect actions to handle a content except an id --- src/Actions.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Actions.ts b/src/Actions.ts index 9b110b3..40d0f2f 100644 --- a/src/Actions.ts +++ b/src/Actions.ts @@ -762,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. From b6040fdf480f6af237fac6564de7df1a9fa68485 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:29:24 +0100 Subject: [PATCH 12/27] [KFI]test(Selection): Fix selection related tests --- test/ActionsTests.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index d2c0942..6bffbe2 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -596,21 +596,23 @@ describe('Actions', () => { }); }); describe('SelectContent', () => { + const content = repo.CreateContent({ DisplayName: 'My content', Id: 1 }, ContentTypes.Task); it('should return the select content action', () => { const expectedAction = { type: 'SELECT_CONTENT', - id: 1 + content: content } - expect(Actions.SelectContent(1)).to.deep.equal(expectedAction) + expect(Actions.SelectContent(content)).to.deep.equal(expectedAction) }) }) describe('DeSelectContent', () => { + const content = repo.CreateContent({ DisplayName: 'My content', Id: 1 }, ContentTypes.Task); it('should return the deselect content action', () => { const expectedAction = { type: 'DESELECT_CONTENT', - id: 1 + content: content } - expect(Actions.DeSelectContent(1)).to.deep.equal(expectedAction) + expect(Actions.DeSelectContent(content)).to.deep.equal(expectedAction) }) }) describe('ClearSelection', () => { From 0ef29d2c58efbc0c9c5268390edf04dd86546f8f Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:29:56 +0100 Subject: [PATCH 13/27] [KFI]test(Selection): Fix selected reducer tests --- test/ReducersTests.ts | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index 274fb50..d11db03 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -749,34 +749,56 @@ describe('Reducers', () => { }); }) describe('selected reducer', () => { + let repo: Mocks.MockRepository = new Mocks.MockRepository(); + it('should return the initial state', () => { expect(Reducers.selected(undefined, {})).to.deep.equal([]); }); it('should return an array with one item with the id 1', () => { + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 1 + }, ContentTypes.Task) const action = { type: 'SELECT_CONTENT', - id: 1 + content: content } expect(Reducers.selected(undefined, action)).to.deep.equal([1]); }) it('should return an array with two items with the id 1 and 2', () => { + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 2 + }, ContentTypes.Task) const action = { type: 'SELECT_CONTENT', - id: 2 + content: content } expect(Reducers.selected([1], action)).to.deep.equal([1, 2]); }) it('should return an array with one item with the id 1', () => { + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 2 + }, ContentTypes.Task) const action = { type: 'DESELECT_CONTENT', - id: 2 + content: content } expect(Reducers.selected([1, 2], action)).to.deep.equal([1]); }) it('should return an empty array', () => { + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 1 + }, ContentTypes.Task) const action = { type: 'DESELECT_CONTENT', - id: 1 + content: content } expect(Reducers.selected([1], action)).to.deep.equal([]); }) From d274b73f7009ec0817f9d0b93a67e69f44dd8753 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:30:33 +0100 Subject: [PATCH 14/27] [KFI]feat(Selection): Add a new reducer to hold and handle selected content items for batch actions --- src/Reducers.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index 84a8637..16a3d6f 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -1,4 +1,3 @@ -import { normalize } from 'normalizr'; import { combineReducers } from 'redux'; import { Authentication } from 'sn-client-js'; @@ -205,7 +204,10 @@ 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 (Object).assign({}, state, action.response.entities.entities); } switch (action.type) { @@ -591,9 +593,9 @@ export module Reducers { export const selected = (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 [] @@ -601,6 +603,18 @@ export module Reducers { return state } } + export const selectedContent = (state = Object, action) => { + switch (action.type) { + case 'DESELECT_CONTENT': + let res = Object.assign({}, state); + delete res[action.content]; + return res; + case 'SELECT_CONTENT': + return (Object).assign({}, state, action.content); + default: + return state; + } + } /** * Reducer to handle Actions on the OdataBatchResponse Object. * @param {Array} state Represents the current state. From 4080a240bbb406cc59438a3a0d47835c56ffb759 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:49:08 +0100 Subject: [PATCH 15/27] [KFI]fix(Selection): Fix selectedContentItems reducer and its tests --- src/Reducers.ts | 16 ++++-- test/ReducersTests.ts | 125 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 11 deletions(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index 16a3d6f..91d140a 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -590,7 +590,7 @@ export module Reducers { * @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.content.Id] @@ -603,18 +603,26 @@ export module Reducers { return state } } - export const selectedContent = (state = Object, action) => { + export const selectedContentItems = (state = {}, action) => { switch (action.type) { case 'DESELECT_CONTENT': let res = Object.assign({}, state); - delete res[action.content]; + delete res[action.content.Id]; return res; case 'SELECT_CONTENT': - return (Object).assign({}, state, action.content); + let obj = {} + obj[action.content.Id] = action.content + return (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. diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index d11db03..586003f 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -750,9 +750,9 @@ describe('Reducers', () => { }) describe('selected reducer', () => { let repo: Mocks.MockRepository = new Mocks.MockRepository(); - + it('should return the initial state', () => { - expect(Reducers.selected(undefined, {})).to.deep.equal([]); + expect(Reducers.selectedIds(undefined, {})).to.deep.equal([]); }); it('should return an array with one item with the id 1', () => { let content = repo.CreateContent({ @@ -764,7 +764,7 @@ describe('Reducers', () => { type: 'SELECT_CONTENT', content: content } - expect(Reducers.selected(undefined, action)).to.deep.equal([1]); + expect(Reducers.selectedIds(undefined, action)).to.deep.equal([1]); }) it('should return an array with two items with the id 1 and 2', () => { let content = repo.CreateContent({ @@ -776,7 +776,7 @@ describe('Reducers', () => { type: 'SELECT_CONTENT', content: content } - expect(Reducers.selected([1], action)).to.deep.equal([1, 2]); + expect(Reducers.selectedIds([1], action)).to.deep.equal([1, 2]); }) it('should return an array with one item with the id 1', () => { let content = repo.CreateContent({ @@ -788,7 +788,7 @@ describe('Reducers', () => { type: 'DESELECT_CONTENT', content: content } - expect(Reducers.selected([1, 2], action)).to.deep.equal([1]); + expect(Reducers.selectedIds([1, 2], action)).to.deep.equal([1]); }) it('should return an empty array', () => { let content = repo.CreateContent({ @@ -800,13 +800,124 @@ describe('Reducers', () => { type: 'DESELECT_CONTENT', content: content } - expect(Reducers.selected([1], action)).to.deep.equal([]); + expect(Reducers.selectedIds([1], action)).to.deep.equal([]); }) it('should return an empty array', () => { const action = { type: 'CLEAR_SELECTION' } - expect(Reducers.selected([1], action)).to.deep.equal([]); + expect(Reducers.selectedIds([1], action)).to.deep.equal([]); + }) + }) + describe('selectedContent reducer', () => { + let repo: Mocks.MockRepository = new Mocks.MockRepository(); + + it('should return the initial state', () => { + expect(Reducers.selectedContentItems(undefined, {})).to.deep.equal({}); + }); + it('should return an object with one children item with the id 1', () => { + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 1 + }, ContentTypes.Task) + const action = { + type: 'SELECT_CONTENT', + content: content + } + expect(Reducers.selectedContentItems(undefined, action)).to.deep.equal({ 1: content }); + }) + it('should return an object with two items with the id 1 and 2', () => { + const entities = { + 1: { + Id: 1, + DisplayName: 'Some Article', + Status: ['Active'] + } + }; + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 2 + }, ContentTypes.Task) + const action = { + type: 'SELECT_CONTENT', + content: content + } + expect(Reducers.selectedContentItems(entities, action)).to.deep.equal( + { + 1: { + Id: 1, + DisplayName: 'Some Article', + Status: ['Active'] + }, + 2: content + } + ); + }) + it('should return an object with one item with the id 1', () => { + const entities = { + 1: { + Id: 1, + DisplayName: 'Some Article', + Status: ['Active'] + }, + 2: { + Id: 2, + DisplayName: 'Some Article', + Status: ['Active'] + } + }; + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 2 + }, ContentTypes.Task) + const action = { + type: 'DESELECT_CONTENT', + content: content + } + expect(Reducers.selectedContentItems(entities, action)).to.deep.equal( + { + 1: { + Id: 1, + DisplayName: 'Some Article', + Status: ['Active'] + } + } + ); + }) + it('should return an empty object', () => { + const entities = { + 1: { + Id: 1, + DisplayName: 'Some Article', + Status: ['Active'] + } + }; + let content = repo.CreateContent({ + Path: '/Root/Sites/Default_Site/tasks', + Status: Enums.Status.active, + Id: 1 + }, ContentTypes.Task) + const action = { + type: 'DESELECT_CONTENT', + content: content + } + expect(Reducers.selectedContentItems(entities, action)).to.deep.equal({}); + }) + it('should return an empty object', () => { + const entities = { + 1: { + Id: 1, + DisplayName: 'Some Article', + Status: ['Active'] + } + }; + const action = { + type: 'CLEAR_SELECTION' + } + expect(Reducers.selectedContentItems(entities, action)).to.deep.equal({}); }) }) describe('batchResponseError reducer', () => { From 80e79dde9564803ac41d957ef9da29816cff3ae8 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:53:12 +0100 Subject: [PATCH 16/27] [KFI]feat(Selection): Add new functions to return to value of selectedIds and selectedContentItems r --- src/Reducers.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index 91d140a..9ac96c8 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -713,8 +713,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) => { From 6757d7b4f7ed1ea86f20f86403efdeaa4a1eb50d Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 09:53:43 +0100 Subject: [PATCH 17/27] [KFI]test(Selection): Add test for testing new selection reducers --- test/ReducersTests.ts | 47 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index 586003f..83a776f 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -1099,12 +1099,53 @@ describe('Reducers', () => { expect(Reducers.getRepositoryUrl(state)).to.be.eq('https://dmsservice.demo.sensenet.com'); }); }); - describe('getSelectedContent', () => { + describe('getSelectedContentIds', () => { const state = { - selected: [1, 2] + selected: { + ids: [1, 2], + entities: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + } + } } it('should return the value of the selected reducers current state, an array with two items', () => { - expect(Reducers.getSelectedContent(state)).to.be.deep.equal([1, 2]) + expect(Reducers.getSelectedContentIds(state)).to.be.deep.equal([1, 2]) + }) + }) + describe('getSelectedContentItems', () => { + const state = { + selected: { + ids: [1, 2], + entities: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + } + } + } + it('should return the value of the selected reducers current state, an array with two items', () => { + expect(Reducers.getSelectedContentItems(state)).to.be.deep.equal({ + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }) }) }) describe('getOpenedContentId', () => { From 1b276677af3dd2220fc2da254384f1fa6755d0ed Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 12:09:27 +0100 Subject: [PATCH 18/27] [KFI]feat(DeleteBatch): Change id param to contentItems --- src/Actions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Actions.ts b/src/Actions.ts index 40d0f2f..ef08fcb 100644 --- a/src/Actions.ts +++ b/src/Actions.ts @@ -1,6 +1,6 @@ import { normalize } from 'normalizr'; import { Schemas } from './Schema'; -import { Content, IContent, ODataApi, ContentTypes} from 'sn-client-js'; +import { Content, SavedContent, IContent, ODataApi, ContentTypes} from 'sn-client-js'; /** * Module that contains the action creators. @@ -368,9 +368,9 @@ export module Actions { * @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 = (ids: number[], permanently: boolean = false) => ({ + export const DeleteBatch = (contentItems: Object, permanently: boolean = false) => ({ type: 'DELETE_BATCH_REQUEST', - ids, + contentItems, permanently }) /** From 6c50541b21befa1d133eb9761781ca8fa6cfc60a Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 12:09:57 +0100 Subject: [PATCH 19/27] [KFI]test(DeleteBatch): Fix deleteBatch related tests to handle content items as a param --- test/ActionsTests.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index 6bffbe2..cf823d1 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -248,10 +248,28 @@ describe('Actions', () => { it('should create an action to a delete content request', () => { const expectedAction = { type: 'DELETE_BATCH_REQUEST', - ids: [1, 2, 3], + contentItems: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, permanently: false } - expect(Actions.DeleteBatch([1, 2, 3])).to.deep.equal(expectedAction) + expect(Actions.DeleteBatch({ + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + })).to.deep.equal(expectedAction) }); it('should create an action to delete content success', () => { const response = new ODataApi.ODataBatchResponse() From c7f8c0c6a6d989e11086b5e6ff7179845eae87c7 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 12:12:26 +0100 Subject: [PATCH 20/27] [KFI]feat(DeleteBatch): Complete deleteBatch functionality --- src/Epics.ts | 6 ++++-- src/Reducers.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Epics.ts b/src/Epics.ts index 4a1d7d5..ce2f64c 100644 --- a/src/Epics.ts +++ b/src/Epics.ts @@ -194,8 +194,10 @@ 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, action.permanently) + 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) => { return Actions.DeleteBatchSuccess(response); }) diff --git a/src/Reducers.ts b/src/Reducers.ts index 9ac96c8..28167cd 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -187,6 +187,16 @@ export module Reducers { return state case 'DELETE_CONTENT_SUCCESS': return [...state.slice(0, action.index), ...state.slice(action.index + 1)] + case 'DELETE_BATCH_SUCCESS': + if (action.response.d.results.length > 0) { + let newIds = [] + for (let i = 0; i < state.length; i++) { + if (action.response.d.results.indexOf(state[i]) === -1) { + newIds.push(state[i]) + } + } + return newIds + } default: return state; } @@ -215,6 +225,10 @@ export module Reducers { let res = Object.assign({}, state); delete res[action.id]; return res; + case 'DELETE_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 From 56a3458fa1dfab8dc673732113b0fdaacf954c43 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 13:23:48 +0100 Subject: [PATCH 21/27] [KFI]test(DeleteBatch): Fix batch delete related tests --- src/Reducers.ts | 3 ++- test/ReducersTests.ts | 56 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Reducers.ts b/src/Reducers.ts index 28167cd..a1403a6 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -190,8 +190,9 @@ export module Reducers { case 'DELETE_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 (action.response.d.results.indexOf(state[i]) === -1) { + if (deletedIds.indexOf(state[i]) === -1) { newIds.push(state[i]) } } diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index 83a776f..7a5fdb0 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -183,6 +183,31 @@ describe('Reducers', () => { })) .to.be.deep.equal([1, 2, 3]); }); + it('should handle DELETE_BATCH_SUCCESS', () => { + expect(Reducers.ids([1, 2, 3], { + type: 'DELETE_BATCH_SUCCESS', + response: { + 'd': { + 'results': [ + { 'Id': 1 }, + { 'Id': 2 } + ], + 'errors': [] + } + } + })).to.be.deep.equal([3]); + }); + it('should handle DELETE_BATCH_SUCCESS', () => { + expect(Reducers.ids([1, 2, 3], { + type: 'DELETE_BATCH_SUCCESS', + response: { + 'd': { + 'results': [], + 'errors': [] + } + } + })).to.be.deep.equal([1, 2, 3]); + }); }); describe('entities reducer', () => { @@ -286,6 +311,37 @@ describe('Reducers', () => { } ); }); + it('should handle DELETE_BATCH_SUCCESS', () => { + const entities = { + 5122: { + Id: 5122, + DisplayName: 'Some Article', + Status: ['Active'] + }, + 5146: { + Id: 5146, + Displayname: 'Other Article', + Status: ['Completed'] + } + }; + expect(Reducers.entities(entities, { + type: 'DELETE_BATCH_SUCCESS', + response: { + 'd': { + 'results': [ + { 'Id': 5122 } + ], + 'errors': [] + } + } + })).to.be.deep.equal({ + 5146: { + Id: 5146, + Displayname: 'Other Article', + Status: ['Completed'] + } + }); + }); }); describe('isFetching reducer', () => { From 89dee11700e876853d43d09a4afbdb472ccfd872 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 15:16:16 +0100 Subject: [PATCH 22/27] [KFI]fix(BatchActions): Change copy and move batch actions first param to a contenlist object --- src/Actions.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Actions.ts b/src/Actions.ts index ef08fcb..fe4cab5 100644 --- a/src/Actions.ts +++ b/src/Actions.ts @@ -1,6 +1,6 @@ import { normalize } from 'normalizr'; import { Schemas } from './Schema'; -import { Content, SavedContent, IContent, ODataApi, ContentTypes} from 'sn-client-js'; +import { Content, SavedContent, IContent, ODataApi, ContentTypes } from 'sn-client-js'; /** * Module that contains the action creators. @@ -397,9 +397,9 @@ export module Actions { * @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 = (ids: number[], path) => ({ + export const CopyBatch = (contentItems: Object, path: string) => ({ type: 'COPY_BATCH_REQUEST', - ids, + contentItems, path }) /** @@ -426,9 +426,9 @@ export module Actions { * @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 = (ids: number[], path) => ({ + export const MoveBatch = (contentItems = {}, path: string) => ({ type: 'MOVE_BATCH_REQUEST', - ids, + contentItems, path }) /** @@ -722,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 }) From 2e79e514cbfba14b7fa4be9515d3867e88654337 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 15:16:47 +0100 Subject: [PATCH 23/27] [KFI]test(BatchActions): Fix tests that are related to the changed param --- test/ActionsTests.ts | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index cf823d1..def0afc 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -291,10 +291,23 @@ describe('Actions', () => { it('should create an action to a copy multiple content request', () => { const expectedAction = { type: 'COPY_BATCH_REQUEST', - ids: [1, 2, 3], + contentItems: + { + '1': { DisplaName: 'aaa', Id: 1 }, + '2': { DisplaName: 'bbb', Id: 2 } + }, path: '/workspaces' } - expect(Actions.CopyBatch([1, 2, 3], '/workspaces')).to.deep.equal(expectedAction) + expect(Actions.CopyBatch({ + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, '/workspaces')).to.deep.equal(expectedAction) }); it('should create an action to copy multiple content success', () => { const response = new ODataApi.ODataBatchResponse() @@ -316,10 +329,28 @@ describe('Actions', () => { it('should create an action to a move multiple content request', () => { const expectedAction = { type: 'MOVE_BATCH_REQUEST', - ids: [1, 2, 3], + contentItems: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, path: '/workspaces' } - expect(Actions.MoveBatch([1, 2, 3], '/workspaces')).to.deep.equal(expectedAction) + expect(Actions.MoveBatch({ + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, '/workspaces')).to.deep.equal(expectedAction) }); it('should create an action to move multiple content success', () => { const response = new ODataApi.ODataBatchResponse() From c1aa060dc64cc55d9550bed94547c768bd68620e Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 15:17:41 +0100 Subject: [PATCH 24/27] [KFI]feat(MoveBatch): Add move batch action to the ids and entities reducers --- src/Reducers.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Reducers.ts b/src/Reducers.ts index a1403a6..903e216 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -188,6 +188,7 @@ export module Reducers { 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) @@ -227,6 +228,7 @@ export module Reducers { 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; From e21d26a194811ebfe2d9e4caec32603e70b5fe5a Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 15:18:21 +0100 Subject: [PATCH 25/27] [KFI]test(MoveBatch): Add moveBatch action related tests --- test/ReducersTests.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index 7a5fdb0..efd510a 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -208,6 +208,31 @@ describe('Reducers', () => { } })).to.be.deep.equal([1, 2, 3]); }); + it('should handle MOVE_BATCH_SUCCESS', () => { + expect(Reducers.ids([1, 2, 3], { + type: 'MOVE_BATCH_SUCCESS', + response: { + 'd': { + 'results': [ + { 'Id': 1 }, + { 'Id': 2 } + ], + 'errors': [] + } + } + })).to.be.deep.equal([3]); + }); + it('should handle MOVE_BATCH_SUCCESS', () => { + expect(Reducers.ids([1, 2, 3], { + type: 'MOVE_BATCH_SUCCESS', + response: { + 'd': { + 'results': [], + 'errors': [] + } + } + })).to.be.deep.equal([1, 2, 3]); + }); }); describe('entities reducer', () => { From 4696e585093e6d609c3549429c184534c6faf565 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 15:18:50 +0100 Subject: [PATCH 26/27] [KFI]feat(BatchActionEpics): Add move- and copyBatch epics --- src/Epics.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Epics.ts b/src/Epics.ts index ce2f64c..15958e4 100644 --- a/src/Epics.ts +++ b/src/Epics.ts @@ -204,6 +204,40 @@ export module Epics { .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. @@ -403,6 +437,8 @@ export module Epics { updateContentEpic, deleteContentEpic, deleteBatchEpic, + copyBatchEpic, + moveBatchEpic, checkoutContentEpic, checkinContentEpic, publishContentEpic, From b3bda41ebb02427f0d37e295e37dd3655b547c80 Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 7 Nov 2017 15:19:22 +0100 Subject: [PATCH 27/27] [KFI]test(BatchEpics): Add copy- and movebatch epic tests --- test/EpicsTests.ts | 115 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 28 deletions(-) diff --git a/test/EpicsTests.ts b/test/EpicsTests.ts index b235fe3..99021a4 100644 --- a/test/EpicsTests.ts +++ b/test/EpicsTests.ts @@ -277,34 +277,93 @@ describe('Epics', () => { { type: 'DELETE_CONTENT_FAILURE', error: 'error' }]); }) }); - // describe('deleteBatch Epic', () => { - // before(() => { - // initBefores(Epics.deleteBatchEpic) - // }); - - // after(() => { - // epicMiddleware.replaceEpic(Epics.deleteBatchEpic); - // }); - // it('handles the error', () => { - // store.dispatch({ type: 'DELETE_BATCH_REQUEST', ids: [1, 2], permanently: false }); - // expect(store.getActions()).to.be.deep.eq( - // [{ - // type: 'DELETE_BATCH_REQUEST', - // ids: [1, 2], - // permanently: false - // }]); - // }) - // it('handles the error', () => { - // store.dispatch({ type: 'DELETE_BATCH_FAILURE', error: 'error' }); - // expect(store.getActions()).to.be.deep.eq( - // [{ - // type: 'DELETE_BATCH_REQUEST', - // ids: [1, 2], - // permanently: false - // }, - // { type: 'DELETE_BATCH_FAILURE', error: 'error' }]); - // }) - // }); + describe('deleteBatch Epic', () => { + before(() => { + initBefores(Epics.deleteBatchEpic) + }); + + after(() => { + epicMiddleware.replaceEpic(Epics.deleteBatchEpic); + }); + it('handles the error', () => { + store.dispatch({ + type: 'DELETE_BATCH_REQUEST', contentItems: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, permanently: false + }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'DELETE_BATCH_REQUEST', + contentItems: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, + permanently: false + }]); + }) + it('handles the error', () => { + store.dispatch({ type: 'DELETE_BATCH_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'DELETE_BATCH_REQUEST', + contentItems: { + 1: { + DisplaName: 'aaa', + Id: 1 + }, + 2: { + DisplaName: 'bbb', + Id: 2 + } + }, + permanently: false + }, + { type: 'DELETE_BATCH_FAILURE', error: 'error' }]); + }) + }); + describe('copyBatch Epic', () => { + before(() => { + initBefores(Epics.copyBatchEpic) + }); + + after(() => { + epicMiddleware.replaceEpic(Epics.copyBatchEpic); + }); + + it('handles the error', () => { + store.dispatch({ type: 'COPY_BATCH_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ type: 'COPY_BATCH_FAILURE', error: 'error' }]); + }) + }); + describe('moveBatch Epic', () => { + before(() => { + initBefores(Epics.moveBatchEpic) + }); + + after(() => { + epicMiddleware.replaceEpic(Epics.moveBatchEpic); + }); + + it('handles the error', () => { + store.dispatch({ type: 'MOVE_BATCH_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ type: 'MOVE_BATCH_FAILURE', error: 'error' }]); + }) + }); describe('checkoutContent Epic', () => { before(() => { initBefores(Epics.checkoutContentEpic)