This is an automated email from the ASF dual-hosted git repository. amaranhao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push: new b27842a Replication redux (#1070) b27842a is described below commit b27842a60149ed6be6a3fa6fa7190b86eefb729c Author: garren smith <garren.sm...@gmail.com> AuthorDate: Fri Apr 6 20:18:02 2018 +0200 Replication redux (#1070) * Refactor 'replication' addon to use Redux --- app/addons/auth/actions.js | 31 +- app/addons/documents/rev-browser/actions.js | 1 - app/addons/replication/__tests__/actions.test.js | 17 +- app/addons/replication/__tests__/stores.tests.js | 59 --- app/addons/replication/actions.js | 257 +++++------- app/addons/replication/actiontypes.js | 4 +- app/addons/replication/base.js | 17 +- .../replication/components/newreplication.js | 2 + app/addons/replication/container.js | 138 +++++++ app/addons/replication/controller.js | 143 +++---- app/addons/replication/reducers.js | 381 +++++++++++++++++ app/addons/replication/route.js | 31 +- app/addons/replication/stores.js | 459 --------------------- 13 files changed, 724 insertions(+), 816 deletions(-) diff --git a/app/addons/auth/actions.js b/app/addons/auth/actions.js index 9742bc9..a58cd09 100644 --- a/app/addons/auth/actions.js +++ b/app/addons/auth/actions.js @@ -16,7 +16,6 @@ import ActionTypes from './actiontypes'; import Api from './api'; const { - AUTH_SHOW_PASSWORD_MODAL, AUTH_HIDE_PASSWORD_MODAL, } = ActionTypes; @@ -119,24 +118,20 @@ export const authenticate = (username, password, onSuccess) => { name: username, password: password }) - .then( - () => { - hidePasswordModal(); - onSuccess(username, password); - }, - () => { - FauxtonAPI.addNotification({ - msg: "Your username or password is incorrect.", - type: "error", - clear: true - }); + .then((resp) => { + if (resp.error) { + throw (resp); } - ); -}; - -//This is used in the replication store -export const showPasswordModal = () => { - FauxtonAPI.dispatch({ type: AUTH_SHOW_PASSWORD_MODAL }); + hidePasswordModal(); + onSuccess(username, password); + }) + .catch(() => { + FauxtonAPI.addNotification({ + msg: "Your username or password is incorrect.", + type: "error", + clear: true + }); + }); }; export const hidePasswordModal = () => { diff --git a/app/addons/documents/rev-browser/actions.js b/app/addons/documents/rev-browser/actions.js index 7e9fcee..116af0e 100644 --- a/app/addons/documents/rev-browser/actions.js +++ b/app/addons/documents/rev-browser/actions.js @@ -26,7 +26,6 @@ export const initDiffEditor = (dbName, docId) => dispatch => { const url = FauxtonAPI.urls('databaseBaseURL', 'server', dbName); db = PouchDB(url); - // XXX: we need spec compliant promise support and get rid of jQ "deferreds" Promise.all([db.get(docId), getTree(db, docId)]) .then(([doc, tree]) => { const conflictingRevs = getConflictingRevs(tree.paths, tree.winner, Object.keys(tree.deleted)); diff --git a/app/addons/replication/__tests__/actions.test.js b/app/addons/replication/__tests__/actions.test.js index f12851a..2e090e8 100644 --- a/app/addons/replication/__tests__/actions.test.js +++ b/app/addons/replication/__tests__/actions.test.js @@ -36,6 +36,7 @@ describe("Replication Actions", () => { afterEach(fetchMock.restore); it('creates a new database if it does not exist', (done) => { + const dispatch = () => {}; fetchMock.postOnce('/_replicator', { status: 404, body: { @@ -69,7 +70,7 @@ describe("Replication Actions", () => { replicationTarget: "REPLICATION_TARGET_NEW_LOCAL_DATABASE", replicationType: "", username: "tester" - }); + })(dispatch); //this is not pretty, and might cause some false errors. But its tricky to tell when this test has completed setTimeout(() => { @@ -119,15 +120,15 @@ describe("Replication Actions", () => { }; it('builds up correct state', (done) => { - FauxtonAPI.dispatcher.register(({type, options}) => { + const dispatch = ({type, options}) => { if (ActionTypes.REPLICATION_SET_STATE_FROM_DOC === type) { assert.deepEqual(docState, options); setTimeout(done); } - }); + }; fetchMock.getOnce('/_replicator/7dcea9874a8fcb13c6630a1547001559', doc); - getReplicationStateFrom(doc._id); + getReplicationStateFrom(doc._id)(dispatch); }); }); @@ -171,13 +172,15 @@ describe("Replication Actions", () => { status: 200, body: resp }); - deleteDocs(docs); - FauxtonAPI.dispatcher.register(({type}) => { + + const dispatch = ({type}) => { if (ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS === type) { setTimeout(done); } - }); + }; + + deleteDocs(docs)(dispatch); }); }); }); diff --git a/app/addons/replication/__tests__/stores.tests.js b/app/addons/replication/__tests__/stores.tests.js deleted file mode 100644 index bd5fa36..0000000 --- a/app/addons/replication/__tests__/stores.tests.js +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy of -// the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -import utils from '../../../../test/mocha/testUtils'; -import Stores from '../stores'; -import Constants from '../constants'; - -const assert = utils.assert; -const store = Stores.replicationStore; - -describe('Replication Store', function () { - - afterEach(function () { - store.reset(); - }); - - it('confirm updateFormField updates all fields', function () { - assert.equal(store.getRemoteSource(), ''); - store.updateFormField('remoteSource', 'SOURCE'); - assert.equal(store.getRemoteSource(), 'SOURCE'); - - assert.equal(store.getRemoteTarget(), ''); - store.updateFormField('remoteTarget', 'TARGET'); - assert.equal(store.getRemoteTarget(), 'TARGET'); - - assert.equal(store.getlocalTarget(), ''); - store.updateFormField('localTarget', 'db'); - assert.equal(store.getlocalTarget(), 'db'); - - assert.equal(store.getReplicationType(), Constants.REPLICATION_TYPE.ONE_TIME); - store.updateFormField('replicationType', Constants.REPLICATION_TYPE.CONTINUOUS); - assert.equal(store.getReplicationType(), Constants.REPLICATION_TYPE.CONTINUOUS); - - assert.equal(store.getReplicationDocName(), ''); - store.updateFormField('replicationDocName', 'doc-name'); - assert.equal(store.getReplicationDocName(), 'doc-name'); - - assert.equal(store.getReplicationSource(), ''); - store.updateFormField('replicationSource', 'rsource'); - assert.equal(store.getReplicationSource(), 'rsource'); - - assert.equal(store.getReplicationTarget(), ''); - store.updateFormField('replicationTarget', 'rtarget'); - assert.equal(store.getReplicationTarget(), 'rtarget'); - - assert.equal(store.getlocalSource(), ''); - store.updateFormField('localSource', 'source-db'); - assert.equal(store.getlocalSource(), 'source-db'); - }); - -}); diff --git a/app/addons/replication/actions.js b/app/addons/replication/actions.js index b12ffd7..ba8199c 100644 --- a/app/addons/replication/actions.js +++ b/app/addons/replication/actions.js @@ -10,6 +10,7 @@ // License for the specific language governing permissions and limitations under // the License. import FauxtonAPI from '../../core/api'; +import {get, post} from '../../core/ajax'; import ActionTypes from './actiontypes'; import Helpers from './helpers'; import Constants from './constants'; @@ -22,55 +23,40 @@ import { deleteReplicatesApi, createReplicatorDB } from './api'; -import 'whatwg-fetch'; -function initReplicator (localSource) { - if (localSource) { - FauxtonAPI.dispatch({ +export const initReplicator = (routeLocalSource, localSource) => dispatch => { + if (routeLocalSource && routeLocalSource !== localSource) { + dispatch({ type: ActionTypes.INIT_REPLICATION, options: { - localSource: localSource + localSource: routeLocalSource } }); } +}; - fetch('/_all_dbs', { - credentials: 'include', - headers: { - 'Accept': 'application/json; charset=utf-8', - 'Content-Type': 'application/json' - }, - }) - .then(resp => resp.json()) +export const getDatabasesList = () => dispatch => { + get('/_all_dbs') .then((databases) => { - FauxtonAPI.dispatch({ + dispatch({ type: ActionTypes.REPLICATION_DATABASES_LOADED, options: { - databases: databases + databases } }); }); -} +}; -export const replicate = (params) => { +export const replicate = (params) => dispatch => { const replicationDoc = createReplicationDoc(params); - const promise = fetch('/_replicator', { - method: 'POST', - credentials: 'include', - headers: { - 'Accept': 'application/json; charset=utf-8', - 'Content-Type': 'application/json' - }, - body: JSON.stringify(replicationDoc) - }) - .then(res => res.json()); + const promise = post('/_replicator', replicationDoc); const source = Helpers.getDatabaseLabel(replicationDoc.source); const target = Helpers.getDatabaseLabel(replicationDoc.target); - FauxtonAPI.dispatch({ + dispatch({ type: ActionTypes.REPLICATION_STARTING, }); @@ -82,18 +68,21 @@ export const replicate = (params) => { }); }; - promise.then(json => { - if (!json.ok) { - throw json; - } + promise + .then(json => { + if (!json.ok) { + throw json; + } - FauxtonAPI.addNotification({ - msg: `Replication from <code>${decodeURIComponent(source)}</code> to <code>${decodeURIComponent(target)}</code> has been scheduled.`, - type: 'success', - escape: false, - clear: true - }); - }) + FauxtonAPI.addNotification({ + msg: `Replication from <code>${decodeURIComponent(source)}</code> to <code>${decodeURIComponent(target)}</code> has been scheduled.`, + type: 'success', + escape: false, + clear: true + }); + + dispatch(getReplicationActivity()); + }) .catch(json => { if (json.error && json.error === "not_found") { return createReplicatorDB().then(() => { @@ -106,47 +95,51 @@ export const replicate = (params) => { }); }; -function updateFormField (fieldName, value) { - FauxtonAPI.dispatch({ +export const updateFormField = (fieldName, value) => { + return { type: ActionTypes.REPLICATION_UPDATE_FORM_FIELD, options: { fieldName: fieldName, value: value } - }); -} + }; +}; -function clearReplicationForm () { - FauxtonAPI.dispatch({ type: ActionTypes.REPLICATION_CLEAR_FORM }); -} +export const clearReplicationForm = () => { + return { type: ActionTypes.REPLICATION_CLEAR_FORM }; +}; -const getReplicationActivity = (supportNewApi) => { - FauxtonAPI.dispatch({ +export const getReplicationActivity = () => dispatch => { + dispatch({ type: ActionTypes.REPLICATION_FETCHING_STATUS, }); - fetchReplicationDocs(supportNewApi).then(docs => { - FauxtonAPI.dispatch({ - type: ActionTypes.REPLICATION_STATUS, - options: docs + supportNewApi() + .then(supportNewApi => { + return fetchReplicationDocs(supportNewApi); + }) + .then(docs => { + dispatch({ + type: ActionTypes.REPLICATION_STATUS, + options: docs + }); }); - }); }; -const getReplicateActivity = () => { +export const getReplicateActivity = () => dispatch => { supportNewApi() .then(newApi => { if (!newApi) { return; } - FauxtonAPI.dispatch({ + dispatch({ type: ActionTypes.REPLICATION_FETCHING_REPLICATE_STATUS, }); fetchReplicateInfo() .then(replicateInfo => { - FauxtonAPI.dispatch({ + dispatch({ type: ActionTypes.REPLICATION_REPLICATE_STATUS, options: replicateInfo }); @@ -154,59 +147,59 @@ const getReplicateActivity = () => { }); }; -const filterDocs = (filter) => { - FauxtonAPI.dispatch({ +export const filterDocs = (filter) => { + return { type: ActionTypes.REPLICATION_FILTER_DOCS, options: filter - }); + }; }; -const filterReplicate = (filter) => { - FauxtonAPI.dispatch({ +export const filterReplicate = (filter) => { + return { type: ActionTypes.REPLICATION_FILTER_REPLICATE, options: filter - }); + }; }; -const selectAllDocs = () => { - FauxtonAPI.dispatch({ +export const selectAllDocs = () => { + return { type: ActionTypes.REPLICATION_TOGGLE_ALL_DOCS - }); + }; }; -const selectDoc = (id) => { - FauxtonAPI.dispatch({ +export const selectDoc = (id) => { + return { type: ActionTypes.REPLICATION_TOGGLE_DOC, options: id - }); + }; }; -const selectAllReplicates = () => { - FauxtonAPI.dispatch({ +export const selectAllReplicates = () => { + return { type: ActionTypes.REPLICATION_TOGGLE_ALL_REPLICATE - }); + }; }; -const selectReplicate = (id) => { - FauxtonAPI.dispatch({ +export const selectReplicate = (id) => { + return { type: ActionTypes.REPLICATION_TOGGLE_REPLICATE, options: id - }); + }; }; -const clearSelectedDocs = () => { - FauxtonAPI.dispatch({ +export const clearSelectedDocs = () => { + return { type: ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS - }); + }; }; -const clearSelectedReplicates = () => { - FauxtonAPI.dispatch({ +export const clearSelectedReplicates = () => { + return { type: ActionTypes.REPLICATION_CLEAR_SELECTED_REPLICATES - }); + }; }; -export const deleteDocs = (docs) => { +export const deleteDocs = (docs) => dispatch => { const bulkDocs = docs.map(({raw: doc}) => { doc._deleted = true; return doc; @@ -219,20 +212,12 @@ export const deleteDocs = (docs) => { clear: true }); - fetch('/_replicator/_bulk_docs', { - credentials: 'include', - headers: { - 'Accept': 'application/json; charset=utf-8', - 'Content-Type': 'application/json' - }, - method: 'POST', - body: JSON.stringify({docs: bulkDocs}) - }) + post('/_replicator/_bulk_docs', {docs: bulkDocs}, {raw: true}) .then(resp => { if (!resp.ok) { throw resp; } - return resp.json(); + return resp; }) .then(() => { @@ -248,8 +233,8 @@ export const deleteDocs = (docs) => { clear: true }); - clearSelectedDocs(); - getReplicationActivity(); + dispatch(clearSelectedDocs()); + dispatch(getReplicationActivity()); }) .catch(resp => { resp.json() @@ -264,7 +249,7 @@ export const deleteDocs = (docs) => { }); }; -const deleteReplicates = (replicates) => { +export const deleteReplicates = (replicates) => dispatch => { FauxtonAPI.addNotification({ msg: `Deleting _replicate${replicates.length > 1 ? 's' : ''}.`, type: 'success', @@ -279,8 +264,8 @@ const deleteReplicates = (replicates) => { msg = `Replication <code>${replicates[0]._id}</code> has been deleted`; } - clearSelectedReplicates(); - getReplicateActivity(); + dispatch(clearSelectedReplicates()); + dispatch(getReplicateActivity()); FauxtonAPI.addNotification({ msg: msg, @@ -298,19 +283,12 @@ const deleteReplicates = (replicates) => { }); }; -export const getReplicationStateFrom = (id) => { - FauxtonAPI.dispatch({ +export const getReplicationStateFrom = (id) => dispatch => { + dispatch({ type: ActionTypes.REPLICATION_FETCHING_FORM_STATE }); - fetch(`/_replicator/${encodeURIComponent(id)}`, { - credentials: 'include', - headers: { - 'Accept': 'application/json; charset=utf-8', - }, - method: 'GET' - }) - .then(resp => resp.json()) + get(`/_replicator/${encodeURIComponent(id)}`) .then((doc) => { const stateDoc = { replicationDocName: doc._id, @@ -338,7 +316,7 @@ export const getReplicationStateFrom = (id) => { stateDoc.remoteTarget = decodeFullUrl(targetUrl); } - FauxtonAPI.dispatch({ + dispatch({ type: ActionTypes.REPLICATION_SET_STATE_FROM_DOC, options: stateDoc }); @@ -353,65 +331,42 @@ export const getReplicationStateFrom = (id) => { }); }; -const showConflictModal = () => { - FauxtonAPI.dispatch({ +export const showConflictModal = () => { + return { type: ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL - }); + }; }; -const hideConflictModal = () => { - FauxtonAPI.dispatch({ +export const hideConflictModal = () => { + return { type: ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL - }); + }; }; -const changeActivitySort = (sort) => { - FauxtonAPI.dispatch({ +export const changeActivitySort = (sort) => { + return { type: ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT, options: sort - }); -}; - -const changeTabSection = (newSection, url) => { - FauxtonAPI.dispatch({ - type: ActionTypes.REPLICATION_CHANGE_TAB_SECTION, - options: newSection - }); - - if (url) { - FauxtonAPI.navigate(url, {trigger: false}); - } + }; }; -const checkForNewApi = () => { +export const checkForNewApi = () => dispatch => { supportNewApi().then(newApi => { - FauxtonAPI.dispatch({ + dispatch({ type: ActionTypes.REPLICATION_SUPPORT_NEW_API, options: newApi }); }); }; -export default { - checkForNewApi, - initReplicator, - replicate, - updateFormField, - clearReplicationForm, - getReplicationActivity, - filterDocs, - selectAllDocs, - selectDoc, - deleteDocs, - getReplicationStateFrom, - showConflictModal, - hideConflictModal, - changeActivitySort, - clearSelectedDocs, - changeTabSection, - getReplicateActivity, - filterReplicate, - selectReplicate, - selectAllReplicates, - deleteReplicates +export const showPasswordModal = () => { + return { + type: ActionTypes.REPLICATION_SHOW_PASSWORD_MODAL + }; +}; + +export const hidePasswordModal = () => { + return { + type: ActionTypes.REPLICATION_HIDE_PASSWORD_MODAL + }; }; diff --git a/app/addons/replication/actiontypes.js b/app/addons/replication/actiontypes.js index 5089ea4..7143868 100644 --- a/app/addons/replication/actiontypes.js +++ b/app/addons/replication/actiontypes.js @@ -36,5 +36,7 @@ export default { REPLICATION_TOGGLE_ALL_REPLICATE: 'REPLICATION_TOGGLE_ALL_REPLICATE', REPLICATION_TOGGLE_REPLICATE: 'REPLICATION_TOGGLE_REPLICATE', REPLICATION_CLEAR_SELECTED_REPLICATES: 'REPLICATION_CLEAR_SELECTED_REPLICATES', - REPLICATION_FETCHING_FORM_STATE: 'REPLICATION_FETCHING_FORM_STATE' + REPLICATION_FETCHING_FORM_STATE: 'REPLICATION_FETCHING_FORM_STATE', + REPLICATION_HIDE_PASSWORD_MODAL: 'REPLICATION_HIDE_PASSWORD_MODAL', + REPLICATION_SHOW_PASSWORD_MODAL: 'REPLICATION_SHOW_PASSWORD_MODAL' }; diff --git a/app/addons/replication/base.js b/app/addons/replication/base.js index 54c8f85..e4b966b 100644 --- a/app/addons/replication/base.js +++ b/app/addons/replication/base.js @@ -13,22 +13,23 @@ import FauxtonAPI from '../../core/api'; import replication from './route'; import './assets/less/replication.less'; -import Actions from './actions'; +import { checkForNewApi } from './actions'; +import replicationReducer from './reducers'; replication.initialize = function () { FauxtonAPI.addHeaderLink({ title: 'Replication', href: '#/replication', icon: 'fonticon-replicate' }); FauxtonAPI.session.isAuthenticated().then(() => { - Actions.checkForNewApi(); + checkForNewApi(); }); }; +FauxtonAPI.addReducers({ + replication: replicationReducer +}); + FauxtonAPI.registerUrls('replication', { - app: (db) => { - return '#/replication/_create/' + db; - }, - api: () => { - return window.location.origin + '/_replicator'; - } + app: (db) => '#/replication/_create/' + db, + api: () => window.location.origin + '/_replicator' }); export default replication; diff --git a/app/addons/replication/components/newreplication.js b/app/addons/replication/components/newreplication.js index 1b64008..f1a82a0 100644 --- a/app/addons/replication/components/newreplication.js +++ b/app/addons/replication/components/newreplication.js @@ -156,6 +156,7 @@ export default class NewReplicationController extends React.Component { } } + this.props.hidePasswordModal(); this.props.replicate({ replicationTarget, replicationSource, @@ -263,6 +264,7 @@ export default class NewReplicationController extends React.Component { modalMessage={<p>{app.i18n.en_US['replication-password-modal-text']}</p>} submitBtnLabel="Start Replication" headerTitle={app.i18n.en_US['replication-password-modal-header']} + onClose={this.props.hidePasswordModal} onSuccess={this.submit} /> <ConflictModal visible={conflictModalVisible} diff --git a/app/addons/replication/container.js b/app/addons/replication/container.js new file mode 100644 index 0000000..7320ae3 --- /dev/null +++ b/app/addons/replication/container.js @@ -0,0 +1,138 @@ +import { connect } from 'react-redux'; +import ReplicationController from './controller'; + +import { + checkForNewApi, + updateFormField, + clearReplicationForm, + initReplicator, + getReplicationStateFrom, + getReplicateActivity, + getReplicationActivity, + getDatabasesList, + showPasswordModal, + hidePasswordModal, + showConflictModal, + hideConflictModal, + replicate, + filterReplicate, + filterDocs, + selectDoc, + deleteDocs, + selectAllDocs, + changeActivitySort, + deleteReplicates, + selectAllReplicates, + selectReplicate +} from './actions'; + +import { + isLoading, + isActivityLoading, + getDatabases, + isAuthenticated, + getReplicationSource, + getLocalSource, + isLocalSourceKnown, + getRemoteSource, + getReplicationTarget, + getLocalTarget, + isLocalTargetKnown, + getRemoteTarget, + isPasswordModalVisible, + isConflictModalVisible, + getReplicationType, + getReplicationDocName, + getSubmittedNoChange, + getFilteredReplicationStatus, + getStatusFilter, + getReplicateFilter, + getAllDocsSelected, + getSomeDocsSelected, + getUsername, + getPassword, + getActivitySort, + getCheckingApi, + supportNewApi, + isReplicateInfoLoading, + getAllReplicateSelected, + getReplicateInfo, + someReplicateSelected +} from './reducers'; + +const mapStateToProps = ({replication}, ownProps) => { + return { + routeLocalSource: ownProps.routeLocalSource, + replicationId: ownProps.replicationId, + tabSection: ownProps.section, + loading: isLoading(replication), + activityLoading: isActivityLoading(replication), + databases: getDatabases(replication), + authenticated: isAuthenticated(replication), + + // source fields + replicationSource: getReplicationSource(replication), + localSource: getLocalSource(replication), + localSourceKnown: isLocalSourceKnown(replication), + remoteSource: getRemoteSource(replication), + + // target fields + replicationTarget: getReplicationTarget(replication), + localTarget: getLocalTarget(replication), + localTargetKnown: isLocalTargetKnown(replication), + remoteTarget: getRemoteTarget(replication), + + // other + passwordModalVisible: isPasswordModalVisible(replication), + isConflictModalVisible: isConflictModalVisible(replication), + replicationType: getReplicationType(replication), + replicationDocName: getReplicationDocName(replication), + submittedNoChange: getSubmittedNoChange(replication), + statusDocs: getFilteredReplicationStatus(replication), + statusFilter: getStatusFilter(replication), + replicateFilter: getReplicateFilter(replication), + allDocsSelected: getAllDocsSelected(replication), + someDocsSelected: getSomeDocsSelected(replication), + username: getUsername(replication), + password: getPassword(replication), + activitySort: getActivitySort(replication), + checkingApi: getCheckingApi(replication), + supportNewApi: supportNewApi(replication), + replicateLoading: isReplicateInfoLoading(replication), + replicateInfo: getReplicateInfo(replication), + allReplicateSelected: getAllReplicateSelected(replication), + someReplicateSelected: someReplicateSelected(replication) + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + checkForNewApi: () => dispatch(checkForNewApi()), + updateFormField: (fieldName) => (value) => { + dispatch(updateFormField(fieldName, value)); + }, + clearReplicationForm: () => dispatch(clearReplicationForm()), + initReplicator: (localSource) => dispatch(initReplicator(localSource)), + getReplicationActivity: () => dispatch(getReplicationActivity()), + getReplicateActivity: () => dispatch(getReplicateActivity()), + getReplicationStateFrom: (id) => dispatch(getReplicationStateFrom(id)), + getDatabasesList: () => dispatch(getDatabasesList()), + showPasswordModal: () => dispatch(showPasswordModal()), + hidePasswordModal: () => dispatch(hidePasswordModal()), + replicate: (params) => dispatch(replicate(params)), + showConflictModal: () => dispatch(showConflictModal()), + hideConflictModal: () => dispatch(hideConflictModal()), + filterReplicate: (filter) => dispatch(filterReplicate(filter)), + filterDocs: (filter) => dispatch(filterDocs(filter)), + selectDoc: (doc) => dispatch(selectDoc(doc)), + deleteDocs: (docs) => dispatch(deleteDocs(docs)), + selectAllDocs: () => dispatch(selectAllDocs()), + changeActivitySort: (sort) => dispatch(changeActivitySort(sort)), + selectAllReplicates: () => dispatch(selectAllReplicates()), + deleteReplicates: (replicates) => dispatch(deleteReplicates(replicates)), + selectReplicate: (replicate) => dispatch(selectReplicate(replicate)) + }; +}; + + +export default connect(mapStateToProps, mapDispatchToProps)(ReplicationController); diff --git a/app/addons/replication/controller.js b/app/addons/replication/controller.js index dfefabc..117714a 100644 --- a/app/addons/replication/controller.js +++ b/app/addons/replication/controller.js @@ -9,11 +9,9 @@ // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. +import FauxtonAPI from '../../core/api'; import React from 'react'; -import Stores from './stores'; -import Actions from './actions'; import Helpers from '../../helpers'; -import {showPasswordModal} from '../auth/actions'; import Components from '../components/react-components'; import NewReplication from './components/newreplication'; import Activity from './components/activity'; @@ -24,87 +22,41 @@ import ReplicateActivity from './components/replicate-activity'; const {LoadLines, Polling, RefreshBtn} = Components; -const store = Stores.replicationStore; - export default class ReplicationController extends React.Component { - constructor (props) { - super(props); - this.state = this.getStoreState(); - } - - getStoreState () { - return { - loading: store.isLoading(), - activityLoading: store.isActivityLoading(), - databases: store.getDatabases(), - authenticated: store.isAuthenticated(), - - // source fields - replicationSource: store.getReplicationSource(), - localSource: store.getlocalSource(), - localSourceKnown: store.isLocalSourceKnown(), - remoteSource: store.getRemoteSource(), - - // target fields - replicationTarget: store.getReplicationTarget(), - localTarget: store.getlocalTarget(), - localTargetKnown: store.isLocalTargetKnown(), - remoteTarget: store.getRemoteTarget(), - - // other - passwordModalVisible: store.isPasswordModalVisible(), - showConflictModal: store.isConflictModalVisible(), - replicationType: store.getReplicationType(), - replicationDocName: store.getReplicationDocName(), - submittedNoChange: store.getSubmittedNoChange(), - statusDocs: store.getFilteredReplicationStatus(), - statusFilter: store.getStatusFilter(), - replicateFilter: store.getReplicateFilter(), - allDocsSelected: store.getAllDocsSelected(), - someDocsSelected: store.someDocsSelected(), - username: store.getUsername(), - password: store.getPassword(), - activitySort: store.getActivitySort(), - tabSection: store.getTabSection(), - checkingApi: store.checkingAPI(), - supportNewApi: store.supportNewApi(), - replicateLoading: store.isReplicateInfoLoading(), - replicateInfo: store.getReplicateInfo(), - allReplicateSelected: store.getAllReplicateSelected(), - someReplicateSelected: store.someReplicateSelected() - }; - } loadReplicationInfo (props, oldProps) { - Actions.initReplicator(props.localSource); + this.props.initReplicator(props.routeLocalSource, props.localSource); this.getAllActivity(); + this.loadReplicationStateFrom(props, oldProps); + } + + loadReplicationStateFrom (props, oldProps) { if (props.replicationId && props.replicationId !== oldProps.replicationId) { - Actions.clearReplicationForm(); - Actions.getReplicationStateFrom(props.replicationId); + this.props.clearReplicationForm(); + this.props.getReplicationStateFrom(props.replicationId); } } getAllActivity () { - Actions.getReplicationActivity(); - Actions.getReplicateActivity(); + this.props.getReplicationActivity(); + this.props.getReplicateActivity(); } componentDidMount () { - store.on('change', this.onChange, this); + this.props.checkForNewApi(); + this.props.getDatabasesList(); this.loadReplicationInfo(this.props, {}); } componentWillReceiveProps (nextProps) { - this.loadReplicationInfo(nextProps, this.props); + this.loadReplicationStateFrom(nextProps, this.props); + if (this.props.tabSection !== 'new replication' && nextProps.tabSection === 'new replication') { + this.props.clearReplicationForm(); + } } componentWillUnmount () { - store.off('change', this.onChange); - Actions.clearReplicationForm(); - } - - onChange () { - this.setState(this.getStoreState()); + this.props.clearReplicationForm(); } showSection () { @@ -112,28 +64,25 @@ export default class ReplicationController extends React.Component { replicationSource, replicationTarget, replicationType, replicationDocName, passwordModalVisible, databases, localSource, remoteSource, remoteTarget, localTarget, statusDocs, statusFilter, loading, allDocsSelected, - someDocsSelected, showConflictModal, localSourceKnown, localTargetKnown, + someDocsSelected, showConflictModal, localSourceKnown, localTargetKnown, updateFormField, username, password, authenticated, activityLoading, submittedNoChange, activitySort, tabSection, - replicateInfo, replicateLoading, replicateFilter, allReplicateSelected, someReplicateSelected - } = this.state; + replicateInfo, replicateLoading, replicateFilter, allReplicateSelected, someReplicateSelected, + showPasswordModal, hidePasswordModal, hideConflictModal, isConflictModalVisible, filterDocs, + filterReplicate, replicate, clearReplicationForm, selectAllDocs, changeActivitySort, selectDoc, + deleteDocs, deleteReplicates, selectAllReplicates, selectReplicate + } = this.props; if (tabSection === 'new replication') { if (loading) { return <LoadLines/>; } - const updateFormField = (field) => { - return (value) => { - Actions.updateFormField(field, value); - }; - }; - return <NewReplication docs={statusDocs} localTargetKnown={localTargetKnown} localSourceKnown={localSourceKnown} - clearReplicationForm={Actions.clearReplicationForm} - replicate={Actions.replicate} + clearReplicationForm={clearReplicationForm} + replicate={replicate} showPasswordModal={showPasswordModal} replicationSource={replicationSource} replicationTarget={replicationTarget} @@ -146,14 +95,15 @@ export default class ReplicationController extends React.Component { remoteTarget={remoteTarget} localTarget={localTarget} updateFormField={updateFormField} - conflictModalVisible={showConflictModal} - hideConflictModal={Actions.hideConflictModal} - showConflictModal={Actions.showConflictModal} + conflictModalVisible={isConflictModalVisible} + hideConflictModal={hideConflictModal} + showConflictModal={showConflictModal} checkReplicationDocID={checkReplicationDocID} authenticated={authenticated} username={username} password={password} submittedNoChange={submittedNoChange} + hidePasswordModal={hidePasswordModal} />; } @@ -165,14 +115,14 @@ export default class ReplicationController extends React.Component { return <ReplicateActivity docs={replicateInfo} filter={replicateFilter} - onFilterChange={Actions.filterReplicate} - selectDoc={Actions.selectReplicate} - selectAllDocs={Actions.selectAllReplicates} + onFilterChange={filterReplicate} + selectDoc={selectReplicate} + selectAllDocs={selectAllReplicates} allDocsSelected={allReplicateSelected} someDocsSelected={someReplicateSelected} activitySort={activitySort} - changeActivitySort={Actions.changeActivitySort} - deleteDocs={Actions.deleteReplicates} + changeActivitySort={changeActivitySort} + deleteDocs={deleteReplicates} />; } @@ -183,19 +133,19 @@ export default class ReplicationController extends React.Component { return <Activity docs={statusDocs} filter={statusFilter} - onFilterChange={Actions.filterDocs} - selectAllDocs={Actions.selectAllDocs} - selectDoc={Actions.selectDoc} + onFilterChange={filterDocs} + selectAllDocs={selectAllDocs} + selectDoc={selectDoc} allDocsSelected={allDocsSelected} someDocsSelected={someDocsSelected} - deleteDocs={Actions.deleteDocs} + deleteDocs={deleteDocs} activitySort={activitySort} - changeActivitySort={Actions.changeActivitySort} + changeActivitySort={changeActivitySort} />; } getHeaderComponents () { - if (this.state.tabSection === 'new replication') { + if (this.props.tabSection === 'new replication') { return null; } let rightHeaderclass = "right-header-flex"; @@ -220,7 +170,7 @@ export default class ReplicationController extends React.Component { } getTabElements () { - const {tabSection} = this.state; + const {tabSection} = this.props; const elements = [ <TabElement key={1} @@ -230,7 +180,7 @@ export default class ReplicationController extends React.Component { /> ]; - if (this.state.supportNewApi) { + if (this.props.supportNewApi) { elements.push( <TabElement key={2} @@ -245,18 +195,19 @@ export default class ReplicationController extends React.Component { } onTabChange (section, url) { - Actions.changeTabSection(section, url); + // this.props.changeTabSection(section, url); + FauxtonAPI.navigate(url); } getCrumbs () { - if (this.state.tabSection === 'new replication') { + if (this.props.tabSection === 'new replication') { return [{'name': 'Job Configuration'}]; } return [{'name': 'Replication'}]; } getTabs () { - if (this.state.tabSection === 'new replication') { + if (this.props.tabSection === 'new replication') { return null; } @@ -268,7 +219,7 @@ export default class ReplicationController extends React.Component { } render () { - const { checkingAPI } = this.state; + const { checkingAPI } = this.props; if (checkingAPI) { return <LoadLines />; diff --git a/app/addons/replication/reducers.js b/app/addons/replication/reducers.js new file mode 100644 index 0000000..545e98d --- /dev/null +++ b/app/addons/replication/reducers.js @@ -0,0 +1,381 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +import ActionTypes from './actiontypes'; +import Constants from './constants'; +import app from "../../app"; + +const setActivitySort = (sort) => { + app.utils.localStorageSet('replication-activity-sort', sort); +}; + +const loadActivitySort = () => { + const defaultSort = { + descending: false, + column: 'statusTime' + + }; + let sort = app.utils.localStorageGet('replication-activity-sort'); + + if (!sort) { + sort = defaultSort; + } + + setActivitySort(sort); + return sort; +}; + +const validFieldMap = { + remoteSource: 'remoteSource', + remoteTarget: 'remoteTarget', + localTarget: 'localTarget', + replicationType: 'replicationType', + replicationDocName: 'replicationDocName', + replicationSource: 'replicationSource', + replicationTarget: 'replicationTarget', + localSource: 'localSource' +}; + +const initialState = { + loading: false, + databases: [], + authenticated: false, + + // source fields + replicationSource: '', + localSource: '', + remoteSource: '', + + // target fields + replicationTarget: '', + localTarget: '', + remoteTarget: '', + + // other + isPasswordModalVisible: false, + isConflictModalVisible: false, + replicationType: Constants.REPLICATION_TYPE.ONE_TIME, + replicationDocName: '', + submittedNoChange: false, + statusDocs: [], + statusFilteredStatusDocs: [], + statusFilter: '', + replicateFilter: '', + allDocsSelected: false, + allReplicateSelected: false, + username: '', + password: '', + activityLoading: false, + tabSection: 'new replication', + supportNewApi: false, + fetchingReplicateInfo: false, + replicateInfo: [], + + checkingAPI: true, + activitySort: loadActivitySort() +}; + +const clearForm = (state) => { + const newState = { + ...state + }; + Object.values(validFieldMap).forEach(field => newState[field] = ''); + return newState; +}; + +const updateFormField = (state, fieldName, value) => { + const updateState = { + ...state, + submittedNoChange: false, + }; + + updateState[validFieldMap[fieldName]] = value; + + return updateState; +}; + +const toggleDoc = (state, id) => { + const doc = state.statusDocs.find(doc => doc._id === id); + if (!doc) { + return state; + } + + doc.selected = !doc.selected; + + return { + ...state, + allDocsSelected: false + }; +}; + +const selectAllDocs = (state) => { + const newState = { + ...state, + allDocsSelected: !state.allDocsSelected + }; + + getFilteredReplicationStatus(newState) + .forEach(doc => doc.selected = newState.allDocsSelected); + + return newState; +}; + +const selectReplicate = (state, id) => { + const newState = { + ...state + }; + + const doc = newState._replicateInfo.find(doc => doc._id === id); + + if (!doc) { + return newState; + } + + doc.selected = !doc.selected; + newState._allReplicateSelected = false; +}; + +const selectAllReplicate = (state) => { + const newState = { + ...state, + allReplicateSelected: !state.allReplicateSelected + }; + + getReplicateInfo(newState).forEach(doc => doc.selected = newState.allReplicateSelected); + return newState; +}; + +const setStateFromDoc = (state, doc) => { + let newState = { + ...state, + loading: false + }; + + return Object.keys(doc).reduce((state, key) => { + return updateFormField(state, key, doc[key]); + }, newState); +}; + + +const replication = (state = initialState, {type, options}) => { + switch (type) { + + case ActionTypes.INIT_REPLICATION: + const newState = { + ...state, + loading: true + }; + + if (options.localSource) { + newState.localSource = options.localSource; + newState.replicationSource = Constants.REPLICATION_SOURCE.LOCAL; + newState.remoteSource = ''; + newState.replicationTarget = ''; + newState.localTarget = ''; + newState.remoteTarget = ''; + } + return newState; + + case ActionTypes.REPLICATION_DATABASES_LOADED: + return { + ...state, + loading: false, + databases: options.databases + }; + + case ActionTypes.REPLICATION_FETCHING_FORM_STATE: + return { + ...state, + loading: true + }; + + case ActionTypes.REPLICATION_UPDATE_FORM_FIELD: + return updateFormField(state, options.fieldName, options.value); + + case ActionTypes.REPLICATION_CLEAR_FORM: + return clearForm(state); + + case ActionTypes.REPLICATION_STARTING: + return { + ...state, + submittedNoChange: true + }; + + case ActionTypes.REPLICATION_FETCHING_STATUS: + return { + ...state, + activityLoading: true + }; + + case ActionTypes.REPLICATION_STATUS: + return { + ...state, + activityLoading: false, + statusDocs: options + }; + + case ActionTypes.REPLICATION_FILTER_DOCS: + return { + ...state, + statusFilter: options + }; + + case ActionTypes.REPLICATION_FILTER_REPLICATE: + return { + ...state, + replicateFilter: options + }; + + case ActionTypes.REPLICATION_TOGGLE_DOC: + return toggleDoc(state, options); + + case ActionTypes.REPLICATION_TOGGLE_ALL_DOCS: + return selectAllDocs(state); + + case ActionTypes.REPLICATION_TOGGLE_REPLICATE: + return selectReplicate(state, options); + + case ActionTypes.REPLICATION_TOGGLE_ALL_REPLICATE: + return selectAllReplicate(state); + + case ActionTypes.REPLICATION_SET_STATE_FROM_DOC: + return setStateFromDoc(state, options); + + case ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL: + return { + ...state, + isConflictModalVisible: true + }; + + case ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL: + return { + ...state, + isConflictModalVisible: false + }; + + case ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT: + setActivitySort(options); + return { + ...state, + activitySort: loadActivitySort() + }; + + case ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS: + return { + ...state, + allDocsSelected: false + }; + + case ActionTypes.REPLICATION_CHANGE_TAB_SECTION: + return { + ...state, + tabSection: options + }; + + case ActionTypes.REPLICATION_SUPPORT_NEW_API: + return { + ...state, + checkingApi: false, + supportNewApi: options + }; + + case ActionTypes.REPLICATION_FETCHING_REPLICATE_STATUS: + return { + ...state, + fetchingReplicateInfo: true, + }; + + case ActionTypes.REPLICATION_REPLICATE_STATUS: + return { + ...state, + fetchingReplicateInfo: false, + replicateInfo: options + }; + + case ActionTypes.REPLICATION_CLEAR_SELECTED_REPLICATES: + return { + ...state, + allReplicateSelected: false + }; + + case ActionTypes.REPLICATION_SHOW_PASSWORD_MODAL: + return { + ...state, + isPasswordModalVisible: true + }; + + case ActionTypes.REPLICATION_HIDE_PASSWORD_MODAL: + return { + ...state, + isPasswordModalVisible: false + }; + + default: + return state; + } +}; + + +export const isLoading = (state) => state.isLoading; +export const isActivityLoading = (state) => state.activityLoading; +export const getDatabases = (state) => state.databases; +export const isAuthenticated = (state) => state.authenticated; + +export const getReplicationSource = (state) => state.replicationSource; +export const getLocalSource = (state) => state.localSource; +export const isLocalSourceKnown = (state) => _.includes(state.databases, state.localSource); +export const getRemoteSource = (state) => state.remoteSource; + + +export const getReplicationTarget = (state) => state.replicationTarget; +export const getLocalTarget = (state) => state.localTarget; +export const isLocalTargetKnown = (state) => _.includes(state.databases, state.localTarget); +export const getRemoteTarget = (state) => state.remoteTarget; + +export const isPasswordModalVisible = (state) => state.isPasswordModalVisible; +export const isConflictModalVisible = (state) => state.isConflictModalVisible; +export const getReplicationType = (state) => state.replicationType; +export const getReplicationDocName = (state) => state.replicationDocName; +export const getSubmittedNoChange = (state) => state.submittedNoChange; + +export const getFilteredReplicationStatus = (state) => { + return state.statusDocs.filter(doc => { + return Object.values(doc).filter(item => { + if (!item) {return null;} + return item.toString().toLowerCase().match(state.statusFilter); + }).length > 0; + }); +}; + +export const getStatusFilter = (state) => state.statusFilter; +export const getReplicateFilter = (state) => state.replicateFilter; +export const getAllDocsSelected = (state) => state.allDocsSelected; +export const getSomeDocsSelected = (state) => getFilteredReplicationStatus(state).some(doc => doc.selected); +export const getUsername = (state) => state.username; +export const getPassword = (state) => state.password; +export const getActivitySort = (state) => state.activitySort; +export const getTabSection = (state) => state.tabSection; +export const getCheckingApi = (state) => state.checkingAPI; +export const supportNewApi = (state) => state.supportNewApi; +export const isReplicateInfoLoading = (state) => state.fetchingReplicateInfo; +export const getAllReplicateSelected = (state) => state.allReplicateSelected; +export const someReplicateSelected = (state) => getReplicateInfo(state).some(doc => doc.selected); + +export const getReplicateInfo = (state) => { + return state.replicateInfo.filter(doc => { + return Object.values(doc).filter(item => { + if (!item) {return false;} + return item.toString().toLowerCase().match(state._replicateFilter); + }).length > 0; + }); +}; + +export default replication; diff --git a/app/addons/replication/route.js b/app/addons/replication/route.js index 187c5e0..efcd01b 100644 --- a/app/addons/replication/route.js +++ b/app/addons/replication/route.js @@ -12,14 +12,13 @@ import React from 'react'; import FauxtonAPI from '../../core/api'; -import ReplicationController from './controller'; -import Actions from './actions'; +import ReplicationController from './container'; const ReplicationRouteObject = FauxtonAPI.RouteObject.extend({ routes: { - 'replication/_create': 'defaultView', - 'replication/_create/:dbname': 'defaultView', - 'replication/id/:id': 'fromId', + 'replication/_create': 'createView', + 'replication/_create/:dbname': 'createView', + 'replication/id/:id': 'createViewFromId', 'replication': 'activityView', 'replication/_replicate': 'replicateView' }, @@ -40,32 +39,32 @@ const ReplicationRouteObject = FauxtonAPI.RouteObject.extend({ ]; }, - defaultView: function (databaseName) { + createView: function (databaseName) { const localSource = databaseName || ''; - Actions.changeTabSection('new replication'); - Actions.clearReplicationForm(); return <ReplicationController - localSource={localSource} + routeLocalSource={localSource} + section={'new replication'} />; }, - fromId: function (replicationId) { - Actions.clearReplicationForm(); - Actions.changeTabSection('new replication'); + createViewFromId: function (replicationId) { return <ReplicationController replicationId={replicationId} + section={'new replication'} />; }, activityView: function () { - Actions.changeTabSection('activity'); - return <ReplicationController/>; + return <ReplicationController + section={"activity"} + />; }, replicateView: function () { - Actions.changeTabSection('_replicate'); - return <ReplicationController/>; + return <ReplicationController + section={"_replicate"} + />; } }); diff --git a/app/addons/replication/stores.js b/app/addons/replication/stores.js deleted file mode 100644 index 5286550..0000000 --- a/app/addons/replication/stores.js +++ /dev/null @@ -1,459 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy of -// the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -import app from "../../app"; -import FauxtonAPI from '../../core/api'; -import ActionTypes from './actiontypes'; -import Constants from './constants'; -import AccountActionTypes from '../auth/actiontypes'; -import _ from 'lodash'; - -// I know this could be done by just adding the _ prefix to the passed field name, I just don't much like relying -// on the var names like that... -const validFieldMap = { - remoteSource: '_remoteSource', - remoteTarget: '_remoteTarget', - localTarget: '_localTarget', - replicationType: '_replicationType', - replicationDocName: '_replicationDocName', - replicationSource: '_replicationSource', - replicationTarget: '_replicationTarget', - localSource: '_localSource' -}; - -const ReplicationStore = FauxtonAPI.Store.extend({ - initialize () { - this.reset(); - }, - - reset () { - this._loading = false; - this._databases = []; - this._authenticated = false; - this._password = ''; - - // source fields - this._replicationSource = ''; - this._localSource = ''; - this._remoteSource = ''; - - // target fields - this._replicationTarget = ''; - this._localTarget = ''; - this._remoteTarget = ''; - - // other - this._isPasswordModalVisible = false; - this._isConflictModalVisible = false; - this._replicationType = Constants.REPLICATION_TYPE.ONE_TIME; - this._replicationDocName = ''; - this._submittedNoChange = false; - this._statusDocs = []; - this._statusFilteredStatusDocs = []; - this._statusFilter = ''; - this._replicateFilter = ''; - this._allDocsSelected = false; - this._allReplicateSelected = false; - this._username = ''; - this._password = ''; - this._activityLoading = false; - this._tabSection = 'new replication'; - this._supportNewApi = true; - - this.loadActivitySort(); - - this._fetchingReplicateInfo = false; - this._replicateInfo = []; - - this._checkingAPI = true; - this._supportNewApi = false; - }, - - supportNewApi () { - return this._supportNewApi; - }, - - checkingAPI () { - return this._checkingAPI; - }, - - getActivitySort () { - return this._activitySort; - }, - - loadActivitySort () { - const defaultSort = { - descending: false, - column: 'statusTime' - }; - let sort = app.utils.localStorageGet('replication-activity-sort'); - - if (!sort) { - sort = defaultSort; - this.setActivitySort(sort); - } - - this._activitySort = sort; - }, - - setActivitySort (sort) { - app.utils.localStorageSet('replication-activity-sort', sort); - this._activitySort = sort; - }, - - isReplicateInfoLoading () { - return this._fetchingReplicateInfo; - }, - - getReplicateInfo () { - return this._replicateInfo.filter(doc => { - return _.values(doc).filter(item => { - if (!item) {return false;} - return item.toString().toLowerCase().match(this._replicateFilter); - }).length > 0; - }); - }, - - setReplicateInfo (info) { - this._replicateInfo = info; - }, - - setCredentials (username, password) { - this._username = username; - this._password = password; - }, - - getUsername () { - return this._username; - }, - - getPassword () { - return this._password; - }, - - getSubmittedNoChange () { - return this._submittedNoChange; - }, - - changeAfterSubmit () { - this._submittedNoChange = false; - }, - - isLoading () { - return this._loading; - }, - - isActivityLoading () { - return this._activityLoading; - }, - - isAuthenticated () { - return this._authenticated; - }, - - getReplicationSource () { - return this._replicationSource; - }, - - getlocalSource () { - return this._localSource; - }, - - isLocalSourceKnown () { - return _.includes(this._databases, this._localSource); - }, - - isLocalTargetKnown () { - return _.includes(this._databases, this._localTarget); - }, - - getReplicationTarget () { - return this._replicationTarget; - }, - - getDatabases () { - return this._databases; - }, - - setDatabases (databases) { - this._databases = databases; - }, - - getReplicationType () { - return this._replicationType; - }, - - getlocalTarget () { - return this._localTarget; - }, - - getReplicationDocName () { - return this._replicationDocName; - }, - - setReplicationStatus (docs) { - this._statusDocs = docs; - }, - - getReplicationStatus () { - return this._statusDocs; - }, - - getFilteredReplicationStatus () { - return this._statusDocs.filter(doc => { - return _.values(doc).filter(item => { - if (!item) {return null;} - return item.toString().toLowerCase().match(this._statusFilter); - }).length > 0; - }); - }, - - selectDoc (id) { - const doc = this._statusDocs.find(doc => doc._id === id); - if (!doc) { - return; - } - - doc.selected = !doc.selected; - this._allDocsSelected = false; - }, - - selectReplicate (id) { - const doc = this._replicateInfo.find(doc => doc._id === id); - if (!doc) { - return; - } - - doc.selected = !doc.selected; - this._allReplicateSelected = false; - }, - - selectAllReplicate () { - this._allReplicateSelected = !this._allReplicateSelected; - this.getReplicateInfo().forEach(doc => doc.selected = this._allReplicateSelected); - }, - - someReplicateSelected () { - return this.getReplicateInfo().some(doc => doc.selected); - }, - - getAllReplicateSelected () { - return this._allReplicateSelected; - }, - - selectAllDocs () { - this._allDocsSelected = !this._allDocsSelected; - this.getFilteredReplicationStatus().forEach(doc => doc.selected = this._allDocsSelected); - }, - - someDocsSelected () { - return this.getFilteredReplicationStatus().some(doc => doc.selected); - }, - - getAllDocsSelected () { - return this._allDocsSelected; - }, - - setStatusFilter (filter) { - this._statusFilter = filter; - }, - - getStatusFilter () { - return this._statusFilter; - }, - - setReplicateFilter (filter) { - this._replicateFilter = filter; - }, - - getReplicateFilter () { - return this._replicateFilter; - }, - // to cut down on boilerplate - updateFormField (fieldName, value) { - this[validFieldMap[fieldName]] = value; - }, - - clearReplicationForm () { - _.values(validFieldMap).forEach(fieldName => this[fieldName] = ''); - }, - - getRemoteSource () { - return this._remoteSource; - }, - - getRemoteTarget () { - return this._remoteTarget; - }, - - isPasswordModalVisible () { - return this._isPasswordModalVisible; - }, - - isConflictModalVisible () { - return this._isConflictModalVisible; - }, - - setStateFromDoc (doc) { - Object.keys(doc).forEach(key => { - this.updateFormField(key, doc[key]); - }); - }, - - getTabSection () { - return this._tabSection; - }, - - dispatch ({type, options}) { - switch (type) { - - case ActionTypes.INIT_REPLICATION: - this._loading = true; - this._localSource = options.localSource; - - if (this._localSource) { - this._replicationSource = Constants.REPLICATION_SOURCE.LOCAL; - this._remoteSource = ''; - this._replicationTarget = ''; - this._localTarget = ''; - this._remoteTarget = ''; - } - break; - - case ActionTypes.REPLICATION_DATABASES_LOADED: - this.setDatabases(options.databases); - this._loading = false; - break; - - case ActionTypes.REPLICATION_FETCHING_FORM_STATE: - this._loading = true; - break; - - case ActionTypes.REPLICATION_UPDATE_FORM_FIELD: - this.changeAfterSubmit(); - this.updateFormField(options.fieldName, options.value); - break; - - case ActionTypes.REPLICATION_CLEAR_FORM: - this.clearReplicationForm(); - break; - - case ActionTypes.REPLICATION_STARTING: - this._submittedNoChange = true; - break; - - case ActionTypes.REPLICATION_FETCHING_STATUS: - this._activityLoading = true; - break; - - case ActionTypes.REPLICATION_STATUS: - this._activityLoading = false; - this.setReplicationStatus(options); - break; - - case ActionTypes.REPLICATION_FILTER_DOCS: - this.setStatusFilter(options); - break; - - case ActionTypes.REPLICATION_FILTER_REPLICATE: - this.setReplicateFilter(options); - break; - - case ActionTypes.REPLICATION_TOGGLE_DOC: - this.selectDoc(options); - break; - - case ActionTypes.REPLICATION_TOGGLE_ALL_DOCS: - this.selectAllDocs(); - break; - - case ActionTypes.REPLICATION_TOGGLE_REPLICATE: - this.selectReplicate(options); - break; - - case ActionTypes.REPLICATION_TOGGLE_ALL_REPLICATE: - this.selectAllReplicate(); - break; - - case ActionTypes.REPLICATION_SET_STATE_FROM_DOC: - this._loading = false; - this.setStateFromDoc(options); - break; - - case ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL: - this._isConflictModalVisible = true; - break; - - case ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL: - this._isConflictModalVisible = false; - break; - - case ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT: - this.setActivitySort(options); - break; - - case ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS: - this._allDocsSelected = false; - break; - - case ActionTypes.REPLICATION_CHANGE_TAB_SECTION: - this._tabSection = options; - break; - - case ActionTypes.REPLICATION_SUPPORT_NEW_API: - this._checkingAPI = false; - this._supportNewApi = options; - break; - - case ActionTypes.REPLICATION_FETCHING_REPLICATE_STATUS: - this._fetchingReplicateInfo = true; - break; - - case ActionTypes.REPLICATION_REPLICATE_STATUS: - this._fetchingReplicateInfo = false; - this.setReplicateInfo(options); - break; - - case ActionTypes.REPLICATION_CLEAR_SELECTED_REPLICATES: - this._allReplicateSelected = false; - break; - - case AccountActionTypes.AUTH_SHOW_PASSWORD_MODAL: - this._isPasswordModalVisible = true; - break; - - case AccountActionTypes.AUTH_HIDE_PASSWORD_MODAL: - this._isPasswordModalVisible = false; - break; - - case AccountActionTypes.AUTH_CREDS_VALID: - this._authenticated = true; - this.setCredentials(options.username, options.password); - break; - - case AccountActionTypes.AUTH_CREDS_INVALID: - this._authenticated = false; - break; - - default: - return; - } - - this.triggerChange(); - } -}); - -const replicationStore = new ReplicationStore(); -replicationStore.dispatchToken = FauxtonAPI.dispatcher.register(replicationStore.dispatch); - -export default { - replicationStore -}; -- To stop receiving notification emails like this one, please contact amaran...@apache.org.