Add option to create target db as partitioned in replication page (#1192)

diff --git a/app/addons/replication/__tests__/actions.test.js b/app/addons/replication/__tests__/actions.test.js
index 969c73e..b9cfa64 100644
--- a/app/addons/replication/__tests__/actions.test.js
+++ b/app/addons/replication/__tests__/actions.test.js
@@ -145,7 +145,8 @@
       "replicationTarget": "REPLICATION_TARGET_EXISTING_LOCAL_DATABASE",
       "localTarget": "boom123",
       "targetAuthType":"BASIC_AUTH",
-      "targetAuth":{"username":"tester", "password":"testerpass"}
+      "targetAuth":{"username":"tester", "password":"testerpass"},
+      "targetDatabasePartitioned": false
     };
 
     it('builds up correct state', (done) => {
@@ -192,6 +193,7 @@
         "localTarget": "boom123",
         "targetAuthType":"TEST_CUSTOM_AUTH",
         "targetAuth":{"creds":"target_user_creds"},
+        "targetDatabasePartitioned": false
       };
       FauxtonAPI.registerExtension('Replication:Auth', {
         typeValue: 'TEST_CUSTOM_AUTH',
diff --git a/app/addons/replication/__tests__/newreplication.test.js b/app/addons/replication/__tests__/newreplication.test.js
index 687d49e..11ee0c6 100644
--- a/app/addons/replication/__tests__/newreplication.test.js
+++ b/app/addons/replication/__tests__/newreplication.test.js
@@ -75,7 +75,7 @@
         remoteSource={"anotherdb"}
         localTarget={""}
         localSource={""}
-        replicationSource={""}
+        replicationSource={Constants.REPLICATION_SOURCE.REMOTE}
         replicationType={""}
         replicationDocName={""}
         conflictModalVisible={false}
diff --git a/app/addons/replication/__tests__/target.test.js b/app/addons/replication/__tests__/target.test.js
new file mode 100644
index 0000000..16b879b
--- /dev/null
+++ b/app/addons/replication/__tests__/target.test.js
@@ -0,0 +1,81 @@
+// 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 React from 'react';
+import { mount } from 'enzyme';
+import { ReplicationTarget } from '../components/target';
+import Constants from '../constants';
+
+describe('ReplicationTarget', () => {
+
+  describe('NewTargetDatabaseOptionsRow', () => {
+
+    const defaultProps = {
+      databases: ["db1"],
+      onTargetChange: () => {},
+      onLocalTargetChange: () => {},
+      onRemoteTargetChange: () => {},
+      remoteTarget: "",
+      localTarget: "",
+      replicationTarget: Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE,
+      onTargetDatabasePartitionedChange: () => {},
+      targetDatabasePartitioned: false,
+      allowNewPartitionedLocalDbs: false
+    };
+
+    it('Does not show partitioned option for existing remote database', () => {
+      const repRemoteTarget = mount(<ReplicationTarget
+        {...defaultProps}
+        replicationTarget={Constants.REPLICATION_TARGET.EXISTING_REMOTE_DATABASE}
+      />);
+
+      expect(repRemoteTarget.find('input#target-db-is-partitioned').exists()).toBe(false);
+    });
+
+    it('Does not show partitioned option for existing local database', () => {
+      const repLocalTarget = mount(<ReplicationTarget
+        {...defaultProps}
+        replicationTarget={Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE}
+      />);
+
+      expect(repLocalTarget.find('input#target-db-is-partitioned').exists()).toBe(false);
+    });
+
+    it('Shows partitioned option for new remote database', () => {
+      const repRemoteTarget = mount(<ReplicationTarget
+        {...defaultProps}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE}
+      />);
+
+      expect(repRemoteTarget.find('input#target-db-is-partitioned').exists()).toBe(true);
+    });
+
+    it('Shows partitioned option for new local database', () => {
+      const repLocalTarget = mount(<ReplicationTarget
+        {...defaultProps}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        allowNewPartitionedLocalDbs={false}
+      />);
+
+      var input = repLocalTarget.find('input#target-db-is-partitioned');
+      expect(input.exists()).toBe(true);
+      expect(input.prop("disabled")).toBeTruthy();
+
+      repLocalTarget.setProps({allowNewPartitionedLocalDbs: true});
+      input = repLocalTarget.find('input#target-db-is-partitioned');
+      expect(input.exists()).toBe(true);
+      expect(input.prop("disabled")).toBeFalsy();
+    });
+
+  });
+
+});
diff --git a/app/addons/replication/actions.js b/app/addons/replication/actions.js
index 724a041..745a361 100644
--- a/app/addons/replication/actions.js
+++ b/app/addons/replication/actions.js
@@ -325,6 +325,13 @@
   return authTypeAndCreds;
 };
 
+const getTargetDatabasePartitioned = (createTargetParams) => {
+  if (createTargetParams && createTargetParams.partitioned === true) {
+    return true;
+  }
+  return false;
+};
+
 export const getReplicationStateFrom = (id) => dispatch => {
   dispatch({
     type: ActionTypes.REPLICATION_FETCHING_FORM_STATE
@@ -365,6 +372,8 @@
       stateDoc.targetAuthType = targetAuth.type;
       stateDoc.targetAuth = targetAuth.creds;
 
+      stateDoc.targetDatabasePartitioned = getTargetDatabasePartitioned(doc.create_target_params);
+
       dispatch({
         type: ActionTypes.REPLICATION_SET_STATE_FROM_DOC,
         options: stateDoc
diff --git a/app/addons/replication/api.js b/app/addons/replication/api.js
index b092e15..75dee5f 100644
--- a/app/addons/replication/api.js
+++ b/app/addons/replication/api.js
@@ -204,10 +204,11 @@
   sourceAuthType,
   sourceAuth,
   targetAuthType,
-  targetAuth
+  targetAuth,
+  targetDatabasePartitioned
 }) => {
   const username = getUsername();
-  return addDocIdAndRev(replicationDocName, _rev, {
+  const replicationDoc = {
     user_ctx: {
       name: username,
       roles: ['_admin', '_reader', '_writer']
@@ -229,7 +230,13 @@
     }),
     create_target: createTarget(replicationTarget),
     continuous: continuous(replicationType),
-  });
+  };
+  if (targetDatabasePartitioned) {
+    replicationDoc.create_target_params = {
+      partitioned: true
+    };
+  }
+  return addDocIdAndRev(replicationDocName, _rev, replicationDoc);
 };
 
 export const removeSensitiveUrlInfo = (url) => {
diff --git a/app/addons/replication/assets/less/replication.less b/app/addons/replication/assets/less/replication.less
index 1619fbb..ec1c0ad 100644
--- a/app/addons/replication/assets/less/replication.less
+++ b/app/addons/replication/assets/less/replication.less
@@ -41,9 +41,29 @@
   width: 400px;
 }
 
+.replication__input-checkbox {
+  display: flex;
+  align-items: center;
+  width: 400px;
+  height: 55px;
+
+  input[type="checkbox"] {
+    width: auto;
+    margin-right: 0.65rem;
+  }
+
+  label {
+    padding-bottom: 10px;
+  }
+
+  &--disabled label {
+    cursor: not-allowed;
+  }
+}
+
 .replication__input-label {
   padding-right: 15px;
-  width: 160px;
+  width: 165px;
   text-align: right;
   margin-top: 12px;
   font-size: 14px;
diff --git a/app/addons/replication/components/newreplication.js b/app/addons/replication/components/newreplication.js
index 38ca807..08da3b3 100644
--- a/app/addons/replication/components/newreplication.js
+++ b/app/addons/replication/components/newreplication.js
@@ -138,6 +138,7 @@
     const {
       remoteTarget,
       remoteSource,
+      replicationSource,
       replicationTarget,
       localTarget,
       localSource,
@@ -174,7 +175,8 @@
     }
 
     //check if remote source/target URL is valid
-    if (!isEmpty(remoteSource)) {
+    const isRemoteSource = replicationSource === Constants.REPLICATION_SOURCE.REMOTE;
+    if (isRemoteSource && !isEmpty(remoteSource)) {
       let errorMessage = '';
       try {
         const url = new URL(remoteSource);
@@ -194,7 +196,9 @@
         return false;
       }
     }
-    if (!isEmpty(remoteTarget)) {
+    const isRemoteTarget = replicationTarget === Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE ||
+      replicationTarget === Constants.REPLICATION_TARGET.EXISTING_REMOTE_DATABASE;
+    if (isRemoteTarget && !isEmpty(remoteTarget)) {
       let errorMessage = '';
       try {
         const url = new URL(remoteTarget);
@@ -244,7 +248,8 @@
       sourceAuthType,
       sourceAuth,
       targetAuthType,
-      targetAuth
+      targetAuth,
+      targetDatabasePartitioned
     } = this.props;
 
     let _rev;
@@ -268,7 +273,8 @@
       sourceAuthType,
       sourceAuth,
       targetAuthType,
-      targetAuth
+      targetAuth,
+      targetDatabasePartitioned
     });
   }
 
@@ -321,6 +327,8 @@
       remoteSource,
       remoteTarget,
       localTarget,
+      targetDatabasePartitioned,
+      allowNewPartitionedLocalDbs,
       updateFormField,
       clearReplicationForm,
       sourceAuthType,
@@ -354,8 +362,11 @@
           databases={databases}
           localTarget={localTarget}
           remoteTarget={remoteTarget}
+          allowNewPartitionedLocalDbs={allowNewPartitionedLocalDbs}
+          targetDatabasePartitioned={targetDatabasePartitioned}
           onRemoteTargetChange={updateFormField('remoteTarget')}
           onLocalTargetChange={updateFormField('localTarget')}
+          onTargetDatabasePartitionedChange={updateFormField('targetDatabasePartitioned')}
         />
         <ReplicationAuth
           credentials={targetAuth}
diff --git a/app/addons/replication/components/target.js b/app/addons/replication/components/target.js
index 4cfe1c4..3bf3b5d 100644
--- a/app/addons/replication/components/target.js
+++ b/app/addons/replication/components/target.js
@@ -9,9 +9,11 @@
 // 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 PropTypes from 'prop-types';
 
+import classnames from 'classnames';
+import PropTypes from 'prop-types';
 import React from 'react';
+import { OverlayTrigger, Tooltip } from 'react-bootstrap';
 import Constants from '../constants';
 import Components from '../../components/react-components';
 import ReactSelect from 'react-select';
@@ -32,7 +34,7 @@
   });
 };
 
-const ReplicationTargetSelect = ({value, onChange}) => {
+const ReplicationTargetSelect = ({ value, onChange }) => {
   return (
     <div className="replication__section">
       <div className="replication__input-label">
@@ -54,7 +56,7 @@
   onChange: PropTypes.func.isRequired
 };
 
-const RemoteTargetReplicationRow = ({onChange, value}) => {
+const RemoteTargetReplicationRow = ({ onChange, value }) => {
   return (
     <div>
       <input
@@ -73,8 +75,8 @@
   onChange: PropTypes.func.isRequired
 };
 
-const ExistingLocalTargetReplicationRow = ({onChange, value, databases}) => {
-  const options = databases.map(db => ({value: db, label: db}));
+const ExistingLocalTargetReplicationRow = ({ onChange, value, databases }) => {
+  const options = databases.map(db => ({ value: db, label: db }));
   return (
     <div id="replication-target-local" className="replication__input-react-select">
       <ReactSelect
@@ -82,7 +84,7 @@
         options={options}
         placeholder="Database name"
         clearable={false}
-        onChange={({value}) => onChange(value)}
+        onChange={({ value }) => onChange(value)}
       />
     </div>
   );
@@ -94,7 +96,7 @@
   onChange: PropTypes.func.isRequired
 };
 
-const NewLocalTargetReplicationRow = ({onChange, value}) =>
+const NewLocalTargetReplicationRow = ({ onChange, value }) =>
   <input
     type="text"
     className="replication__new-input"
@@ -144,7 +146,7 @@
   let targetLabel = 'Name:';
 
   if (replicationTarget === Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE ||
-      replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
+    replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
     targetLabel = 'New database:';
   }
 
@@ -167,9 +169,62 @@
   replicationTarget: PropTypes.string.isRequired
 };
 
+const NewTargetDatabaseOptionsRow = ({
+  replicationTarget,
+  targetDatabasePartitioned,
+  onTargetDatabasePartitionedChange,
+  allowNewPartitionedLocalDbs
+}) => {
+  if (!replicationTarget) {
+    return null;
+  }
+
+  if (replicationTarget !== Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE &&
+    replicationTarget !== Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE) {
+    return null;
+  }
+
+  const disablePartitionedOption = replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE &&
+    !allowNewPartitionedLocalDbs;
+  let msg = disablePartitionedOption ? "Local server does not support partitioned databases" :
+    "Creates a new partitioned database";
+  const tooltip = <Tooltip id="new-db-partitioned-tooltip">{msg}</Tooltip>;
+  const togglePartitioned = () => {
+    onTargetDatabasePartitionedChange(!targetDatabasePartitioned);
+  };
+
+  return (
+    <div className="replication__section">
+      <div className="replication__input-label">New database options:</div>
+      <div className={classnames('replication__input-checkbox', { 'replication__input-checkbox--disabled': disablePartitionedOption})}>
+
+        <input id="target-db-is-partitioned"
+          type="checkbox"
+          value="true"
+          checked={targetDatabasePartitioned}
+          onChange={togglePartitioned}
+          disabled={disablePartitionedOption}
+        />
+
+
+        <OverlayTrigger placement="right" overlay={tooltip}>
+          <label htmlFor="target-db-is-partitioned" >Partitioned</label>
+        </OverlayTrigger>
+      </div >
+    </div>
+  );
+};
+
+NewTargetDatabaseOptionsRow.propTypes = {
+  onTargetDatabasePartitionedChange: PropTypes.func.isRequired,
+  targetDatabasePartitioned: PropTypes.bool.isRequired,
+  replicationTarget: PropTypes.string.isRequired,
+  allowNewPartitionedLocalDbs: PropTypes.bool.isRequired
+};
+
 export class ReplicationTarget extends React.Component {
 
-  render () {
+  render() {
     const {
       replicationTarget,
       onLocalTargetChange,
@@ -177,7 +232,10 @@
       databases,
       localTarget,
       onRemoteTargetChange,
-      remoteTarget
+      remoteTarget,
+      targetDatabasePartitioned,
+      onTargetDatabasePartitionedChange,
+      allowNewPartitionedLocalDbs
     } = this.props;
     return (
       <div>
@@ -194,6 +252,12 @@
           onRemoteTargetChange={onRemoteTargetChange}
           onLocalTargetChange={onLocalTargetChange}
         />
+        <NewTargetDatabaseOptionsRow
+          replicationTarget={replicationTarget}
+          onTargetDatabasePartitionedChange={onTargetDatabasePartitionedChange}
+          targetDatabasePartitioned={targetDatabasePartitioned}
+          allowNewPartitionedLocalDbs={allowNewPartitionedLocalDbs}
+        />
       </div>
     );
   }
diff --git a/app/addons/replication/container.js b/app/addons/replication/container.js
index b146843..5be9079 100644
--- a/app/addons/replication/container.js
+++ b/app/addons/replication/container.js
@@ -56,8 +56,10 @@
   someReplicateSelected
 } from './reducers';
 
-const mapStateToProps = ({replication}, ownProps) => {
+const mapStateToProps = ({replication, databases}, ownProps) => {
   return {
+    allowNewPartitionedLocalDbs: databases.partitionedDatabasesAvailable,
+
     routeLocalSource: ownProps.routeLocalSource,
     replicationId: ownProps.replicationId,
     tabSection: ownProps.section,
@@ -80,6 +82,7 @@
     remoteTarget: getRemoteTarget(replication),
     targetAuthType: replication.targetAuthType,
     targetAuth: replication.targetAuth,
+    targetDatabasePartitioned: replication.targetDatabasePartitioned,
 
     // other
     isConflictModalVisible: isConflictModalVisible(replication),
diff --git a/app/addons/replication/controller.js b/app/addons/replication/controller.js
index cf948d1..0c7cc00 100644
--- a/app/addons/replication/controller.js
+++ b/app/addons/replication/controller.js
@@ -70,7 +70,7 @@
       hideConflictModal, isConflictModalVisible, filterDocs,
       filterReplicate, replicate, clearReplicationForm, selectAllDocs, changeActivitySort, selectDoc,
       deleteDocs, deleteReplicates, selectAllReplicates, selectReplicate,
-      sourceAuthType, sourceAuth, targetAuthType, targetAuth
+      sourceAuthType, sourceAuth, targetAuthType, targetAuth, targetDatabasePartitioned, allowNewPartitionedLocalDbs
     } = this.props;
 
     if (tabSection === 'new replication') {
@@ -97,6 +97,8 @@
         sourceAuth={sourceAuth}
         targetAuthType={targetAuthType}
         targetAuth={targetAuth}
+        targetDatabasePartitioned={targetDatabasePartitioned}
+        allowNewPartitionedLocalDbs={allowNewPartitionedLocalDbs}
         updateFormField={updateFormField}
         conflictModalVisible={isConflictModalVisible}
         hideConflictModal={hideConflictModal}
diff --git a/app/addons/replication/reducers.js b/app/addons/replication/reducers.js
index 15b6a53..40a276a 100644
--- a/app/addons/replication/reducers.js
+++ b/app/addons/replication/reducers.js
@@ -46,7 +46,8 @@
   sourceAuthType: 'sourceAuthType',
   sourceAuth: 'sourceAuth',
   targetAuthType: 'targetAuthType',
-  targetAuth: 'targetAuth'
+  targetAuth: 'targetAuth',
+  targetDatabasePartitioned: 'targetDatabasePartitioned'
 };
 
 const initialState = {
@@ -66,6 +67,7 @@
   remoteTarget: '',
   targetAuthType: Constants.REPLICATION_AUTH_METHOD.NO_AUTH,
   targetAuth: {},
+  targetDatabasePartitioned: false,
 
   // other
   isConflictModalVisible: false,
@@ -97,6 +99,8 @@
   Object.values(validFieldMap).forEach(field => {
     if (field === 'sourceAuth' || field === 'targetAuth') {
       newState[field] = {};
+    } else if (field === 'targetDatabasePartitioned') {
+      newState[field] = false;
     } else {
       newState[field] = '';
     }