[partitioned dbs] Updates to index editor and index clone modal (#1158)
* Support creation of partitioned views
* Update tests
* Minor fixes
* Address comments
diff --git a/app/addons/documents/assets/less/sidenav.less b/app/addons/documents/assets/less/sidenav.less
index df2ab14..8ba51a5 100644
--- a/app/addons/documents/assets/less/sidenav.less
+++ b/app/addons/documents/assets/less/sidenav.less
@@ -257,4 +257,8 @@
}
}
}
+ .ddoc-selector-partitioned {
+ padding-top: 0px;
+ padding-bottom: 16px;
+ }
}
diff --git a/app/addons/documents/assets/less/view-editor.less b/app/addons/documents/assets/less/view-editor.less
index 163c244..5c23149 100644
--- a/app/addons/documents/assets/less/view-editor.less
+++ b/app/addons/documents/assets/less/view-editor.less
@@ -253,3 +253,16 @@
cursor: pointer;
}
}
+
+.ddoc-selector-partitioned {
+ clear: both;
+ padding-top: 16px;
+
+ label.check--disabled {
+ cursor: default
+ }
+}
+
+.reduce-editor-warning {
+ padding-bottom: 1rem;
+}
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index 48be8e3..5fa6613 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -135,8 +135,8 @@
return 'database/' + database + partitionUrlComponent(partitionKey) + '/_design/' + designDoc + '/_view/' + indexName + '/edit';
},
- showView: function (database, designDoc, viewName) {
- return '/database/' + database + '/' + designDoc + '/_view/' + viewName;
+ showView: function (database, partitionKey, designDoc, viewName) {
+ return '/database/' + database + partitionUrlComponent(partitionKey) + '/' + designDoc + '/_view/' + viewName;
},
fragment: function (database, designDoc, viewName) {
diff --git a/app/addons/documents/helpers.js b/app/addons/documents/helpers.js
index 69347aa..a5c5921 100644
--- a/app/addons/documents/helpers.js
+++ b/app/addons/documents/helpers.js
@@ -128,11 +128,22 @@
&& selectedNavItem.indexName);
};
+const isDDocPartitioned = (ddoc, isDbPartitioned) => {
+ // By default a design doc is partitioned if the database is partitioned
+ let isDDocPartitioned = isDbPartitioned;
+ // Check if ddoc is explictly set to not partitioned
+ if (isDbPartitioned && ddoc.options && ddoc.options.partitioned === false) {
+ isDDocPartitioned = false;
+ }
+ return isDDocPartitioned;
+};
+
export default {
getSeqNum,
getNewButtonLinks,
getModifyDatabaseLinks,
getNewDocUrl,
+ isDDocPartitioned,
parseJSON,
truncateDoc,
selectedViewContainsReduceFunction,
diff --git a/app/addons/documents/index-editor/__tests__/components.test.js b/app/addons/documents/index-editor/__tests__/components.test.js
index fde338c..a3d4c20 100644
--- a/app/addons/documents/index-editor/__tests__/components.test.js
+++ b/app/addons/documents/index-editor/__tests__/components.test.js
@@ -11,7 +11,7 @@
// the License.
import React from 'react';
-import {mount} from 'enzyme';
+import {mount, shallow} from 'enzyme';
import sinon from 'sinon';
import FauxtonAPI from '../../../../core/api';
import Views from '../components';
@@ -27,19 +27,20 @@
hasCustomReduce: false,
reduce: null,
reduceSelectedOption: 'NONE',
+ customReducerSupported: true,
updateReduceCode: () => {},
selectReduceChanged: () => {}
};
it('returns null for none', () => {
- const reduceEl = mount(<Views.ReduceEditor
+ const reduceEl = shallow(<Views.ReduceEditor
{...defaultProps}
/>);
expect(reduceEl.instance().getReduceValue()).toBeNull();
});
it('returns built in for built in reduce', () => {
- const reduceEl = mount(<Views.ReduceEditor
+ const reduceEl = shallow(<Views.ReduceEditor
{...defaultProps}
reduce='_sum'
hasReduce={true}
@@ -47,20 +48,38 @@
expect(reduceEl.instance().getReduceValue()).toBe('_sum');
});
+ it('shows warning when custom reduce is not supported', () => {
+ const reduceEl = shallow(<Views.ReduceEditor
+ {...defaultProps}
+ reduce='function() {}'
+ hasReduce={true}
+ customReducerSupported={false}
+ />);
+ expect(reduceEl.find('div.reduce-editor-warning').exists()).toBe(true);
+ });
+
});
});
describe('DesignDocSelector component', () => {
+ const defaultProps = {
+ designDocList: ['_design/test-doc', '_design/test-doc2'],
+ newDesignDocName: '',
+ selectedDesignDocPartitioned: false,
+ isDbPartitioned: false,
+ newDesignDocPartitioned: false,
+ onChangeNewDesignDocName: () => {},
+ onSelectDesignDoc: () => {}
+ };
let selectorEl;
it('calls onSelectDesignDoc on change', () => {
const spy = sinon.spy();
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc', '_design/test-doc2']}
+ {...defaultProps}
selectedDDocName={'new-doc'}
onSelectDesignDoc={spy}
- onChangeNewDesignDocName={() => {}}
/>);
selectorEl.find('.styled-select select').first().simulate('change', {
@@ -74,10 +93,8 @@
it('shows new design doc field when set to new-doc', () => {
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'new-doc'}
- onSelectDesignDoc={() => { }}
- onChangeNewDesignDocName={() => {}}
/>);
expect(selectorEl.find('#new-ddoc-section').length).toBe(1);
@@ -86,10 +103,8 @@
it('hides new design doc field when design doc selected', () => {
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'_design/test-doc'}
- onSelectDesignDoc={() => { }}
- onChangeNewDesignDocName={() => {}}
/>);
expect(selectorEl.find('#new-ddoc-section').length).toBe(0);
@@ -98,10 +113,8 @@
it('always passes validation when design doc selected', () => {
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'_design/test-doc'}
- onSelectDesignDoc={() => { }}
- onChangeNewDesignDocName={() => {}}
/>);
expect(selectorEl.instance().validate()).toBe(true);
@@ -110,11 +123,9 @@
it('fails validation if new doc name entered/not entered', () => {
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'new-doc'}
newDesignDocName=''
- onSelectDesignDoc={() => { }}
- onChangeNewDesignDocName={() => {}}
/>);
// it shouldn't validate at this point: no new design doc name has been entered
@@ -124,11 +135,9 @@
it('passes validation if new doc name entered/not entered', () => {
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'new-doc'}
newDesignDocName='new-doc-name'
- onSelectDesignDoc={() => { }}
- onChangeNewDesignDocName={() => {}}
/>);
expect(selectorEl.instance().validate()).toBe(true);
});
@@ -137,10 +146,8 @@
it('omits doc URL when not supplied', () => {
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'new-doc'}
- onSelectDesignDoc={() => { }}
- onChangeNewDesignDocName={() => {}}
/>);
expect(selectorEl.find('.help-link').length).toBe(0);
});
@@ -149,15 +156,39 @@
const docLink = 'http://docs.com';
selectorEl = mount(
<Views.DesignDocSelector
- designDocList={['_design/test-doc']}
+ {...defaultProps}
selectedDesignDocName={'new-doc'}
- onSelectDesignDoc={() => { }}
docLink={docLink}
- onChangeNewDesignDocName={() => {}}
/>);
expect(selectorEl.find('.help-link').length).toBe(1);
expect(selectorEl.find('.help-link').prop('href')).toBe(docLink);
});
+
+ it('shows Partitioned checkbox only when db is partitioned', () => {
+ selectorEl = mount(
+ <Views.DesignDocSelector
+ {...defaultProps}
+ selectedDesignDocName={'new-doc'}
+ />);
+
+ expect(selectorEl.find('div.ddoc-selector-partitioned').exists()).toBe(false);
+
+ selectorEl.setProps({isDbPartitioned: true});
+ expect(selectorEl.find('div.ddoc-selector-partitioned').exists()).toBe(true);
+ });
+
+ it('calls onChangeNewDesignDocPartitioned when partitioned option changes', () => {
+ const spy = sinon.stub();
+ selectorEl = mount(
+ <Views.DesignDocSelector
+ {...defaultProps}
+ selectedDesignDocName={'new-doc'}
+ isDbPartitioned={true}
+ onChangeNewDesignDocPartitioned={spy}
+ />);
+ selectorEl.find('input#js-ddoc-selector-partitioned').simulate('change');
+ sinon.assert.called(spy);
+ });
});
describe('IndexEditor', () => {
@@ -165,12 +196,16 @@
isLoading: false,
isNewView: false,
isNewDesignDoc: false,
+ isDbPartitioned: false,
viewName: '',
- database: {},
+ database: { safeID: () => 'test_db' },
+ newDesignDocName: '',
+ newDesignDocPartitioned: false,
originalViewName: '',
originalDesignDocName: '',
designDoc: {},
designDocId: '',
+ designDocPartitioned: false,
designDocList: [],
map: '',
reduce: '',
@@ -179,6 +214,7 @@
updateMapCode: () => {},
selectDesignDoc: () => {},
onChangeNewDesignDocName: () => {},
+ changeViewName: () => {},
reduceOptions: [],
reduceSelectedOption: 'NONE',
hasReduce: false,
@@ -187,6 +223,10 @@
selectReduceChanged: () => {}
};
+ afterEach(() => {
+ sinon.restore();
+ });
+
it('calls changeViewName on view name change', () => {
const spy = sinon.spy();
const editorEl = mount(<Views.IndexEditor
@@ -202,4 +242,23 @@
});
sinon.assert.calledWith(spy, 'newViewName');
});
+
+ it('shows warning when trying to save a partitioned view with custom reduce', () => {
+ sinon.stub(FauxtonAPI, 'addNotification');
+ const editorEl = mount(<Views.IndexEditor
+ {...defaultProps}
+ viewName='new-name'
+ designDocId='new-doc'
+ newDesignDocName='test_ddoc'
+ newDesignDocPartitioned={true}
+ isDbPartitioned={true}
+ reduce='function() { /*custom reduce*/ }'
+ />);
+
+ editorEl.find('form.view-query-save').simulate('submit', {
+ preventDefault: () => {}
+ });
+ sinon.assert.calledWithMatch(FauxtonAPI.addNotification, { msg: 'Partitioned views do not support custom reduce functions.' });
+ });
+
});
diff --git a/app/addons/documents/index-editor/__tests__/reducers.test.js b/app/addons/documents/index-editor/__tests__/reducers.test.js
index ee60b4e..c579010 100644
--- a/app/addons/documents/index-editor/__tests__/reducers.test.js
+++ b/app/addons/documents/index-editor/__tests__/reducers.test.js
@@ -11,7 +11,8 @@
// the License.
import Documents from '../../../documents/resources';
-import reducer, { hasCustomReduce, getDesignDocIds } from '../reducers';
+import reducer, { hasCustomReduce, getDesignDocList, getSelectedDesignDocPartitioned,
+ getSaveDesignDoc } from '../reducers';
import ActionTypes from '../actiontypes';
import '../../base';
@@ -252,7 +253,7 @@
it('only filters mango docs', () => {
const newState = reducer(undefined, editAction);
- const designDocs = getDesignDocIds(newState);
+ const designDocs = getDesignDocList(newState);
expect(designDocs.length).toBe(1);
expect(designDocs[0]).toBe('_design/test-doc');
@@ -296,4 +297,55 @@
expect(newState.view.reduce).toBe('_sum');
});
});
+
+ describe('getSelectedDesignDocPartitioned', () => {
+ const designDocs = [
+ {id: '_design/docGlobal', get: () => { return {options: { partitioned: false }}; }},
+ {id: '_design/docPartitioned', get: () => { return {options: { partitioned: true }}; }},
+ {id: '_design/docNoOptions', get: () => { return {};}}
+ ];
+
+ it('returns true for ddocs without partitioned flag on partitioned dbs', () => {
+ const isDbPartitioned = true;
+ const state = { designDocs, designDocId: '_design/docNoOptions' };
+ expect(getSelectedDesignDocPartitioned(state, isDbPartitioned)).toBe(true);
+ });
+
+ it('returns false for ddocs where partitioned is false on partitioned dbs', () => {
+ const isDbPartitioned = true;
+ const state = { designDocs, designDocId: '_design/docGlobal' };
+ expect(getSelectedDesignDocPartitioned(state, isDbPartitioned)).toBe(false);
+ });
+
+ it('returns true for ddocs where partitioned is true on partitioned dbs', () => {
+ const isDbPartitioned = true;
+ const state = { designDocs, designDocId: '_design/docPartitioned' };
+ expect(getSelectedDesignDocPartitioned(state, isDbPartitioned)).toBe(true);
+ });
+
+ it('any ddoc is global on non-partitioned dbs', () => {
+ const isDbPartitioned = false;
+ const state = { designDocs, designDocId: '_design/docGlobal' };
+ expect(getSelectedDesignDocPartitioned(state, isDbPartitioned)).toBe(false);
+
+ const state2 = { designDocs, designDocId: '_design/docPartitioned' };
+ expect(getSelectedDesignDocPartitioned(state2, isDbPartitioned)).toBe(false);
+
+ const state3 = { designDocs, designDocId: '_design/docNoOptions' };
+ expect(getSelectedDesignDocPartitioned(state3, isDbPartitioned)).toBe(false);
+ });
+
+ });
+
+ describe('getSaveDesignDoc', () => {
+
+ it('only sets partitioned flag when db is partitioned', () => {
+ const state = { designDocId: 'new-doc', newDesignDocPartitioned: true, };
+ const ddoc = getSaveDesignDoc(state, true);
+ expect(ddoc.get('options')).toEqual({ partitioned: true });
+
+ const ddoc2 = getSaveDesignDoc(state, false);
+ expect(ddoc2.get('options')).toBeUndefined();
+ });
+ });
});
diff --git a/app/addons/documents/index-editor/actions.js b/app/addons/documents/index-editor/actions.js
index ad261e0..a6cf7e7 100644
--- a/app/addons/documents/index-editor/actions.js
+++ b/app/addons/documents/index-editor/actions.js
@@ -10,7 +10,6 @@
// License for the specific language governing permissions and limitations under
// the License.
-import app from '../../../app';
import FauxtonAPI from '../../../core/api';
import Documents from '../resources';
import ActionTypes from './actiontypes';
@@ -65,7 +64,7 @@
viewInfo.originalViewName !== viewInfo.viewName;
};
-const saveView = (viewInfo) => (dispatch) => {
+const saveView = (viewInfo, navigateToURL) => (dispatch) => {
const designDoc = viewInfo.designDoc;
designDoc.setDdocView(viewInfo.viewName, viewInfo.map, viewInfo.reduce);
@@ -103,8 +102,7 @@
}
SidebarActions.dispatchUpdateDesignDocs(viewInfo.designDocs);
dispatch({ type: ActionTypes.VIEW_SAVED });
- const fragment = FauxtonAPI.urls('view', 'showView', viewInfo.database.safeID(), designDoc.safeID(), app.utils.safeURLName(viewInfo.viewName));
- FauxtonAPI.navigate(fragment, { trigger: true });
+ FauxtonAPI.navigate(navigateToURL, { trigger: true });
}, (xhr) => {
FauxtonAPI.addNotification({
msg: 'Save failed. ' + (xhr.responseJSON ? `Reason: ${xhr.responseJSON.reason}` : ''),
@@ -146,7 +144,8 @@
};
const cloneView = (params) => {
- const targetDesignDoc = getDesignDoc(params.designDocs, params.targetDesignDocName, params.newDesignDocName, params.database);
+ const targetDesignDoc = getDesignDoc(params.designDocs, params.targetDesignDocName, params.newDesignDocName,
+ params.newDesignDocPartitioned, params.database, params.isDbPartitioned);
let indexes = targetDesignDoc.get('views');
if (indexes && _.has(indexes, params.newIndexName)) {
FauxtonAPI.addNotification({
@@ -223,6 +222,15 @@
});
};
+const updateNewDesignDocPartitioned = (isPartitioned) => (dispatch) => {
+ dispatch({
+ type: ActionTypes.DESIGN_DOC_NEW_PARTITIONED_UPDATED,
+ options: {
+ value: isPartitioned
+ }
+ });
+};
+
// safely deletes an index of any type. It only deletes the actual design doc if there are no
// other indexes of any type left in the doc
const safeDeleteIndex = (designDoc, designDocs, indexPropName, indexName, options) => {
@@ -277,14 +285,18 @@
}).dDocModel();
};
-const getDesignDoc = (designDocs, targetDesignDocName, newDesignDocName, database) => {
+const getDesignDoc = (designDocs, targetDesignDocName, newDesignDocName, newDesignDocPartitioned, database, isDbPartitioned) => {
if (targetDesignDocName === 'new-doc') {
const doc = {
"_id": "_design/" + newDesignDocName,
"views": {},
"language": "javascript"
};
- return new Documents.Doc(doc, { database: database });
+ const dDoc = new Documents.Doc(doc, { database: database });
+ if (isDbPartitioned) {
+ dDoc.setDDocPartitionedOption(newDesignDocPartitioned);
+ }
+ return dDoc;
}
const foundDoc = designDocs.find(function (ddoc) {
@@ -314,5 +326,6 @@
updateMapCode,
updateReduceCode,
selectDesignDoc,
- updateNewDesignDocName
+ updateNewDesignDocName,
+ updateNewDesignDocPartitioned
};
diff --git a/app/addons/documents/index-editor/actiontypes.js b/app/addons/documents/index-editor/actiontypes.js
index 920bd9f..ef746df 100644
--- a/app/addons/documents/index-editor/actiontypes.js
+++ b/app/addons/documents/index-editor/actiontypes.js
@@ -19,6 +19,7 @@
VIEW_CREATED: 'VIEW_CREATED',
DESIGN_DOC_CHANGE: 'DESIGN_DOC_CHANGE',
DESIGN_DOC_NEW_NAME_UPDATED: 'DESIGN_DOC_NEW_NAME_UPDATED',
+ DESIGN_DOC_NEW_PARTITIONED_UPDATED: 'DESIGN_DOC_NEW_PARTITIONED_UPDATED',
NEW_DESIGN_DOC: 'NEW_DESIGN_DOC',
VIEW_NAME_CHANGE: 'VIEW_NAME_CHANGE',
VIEW_ADD_DESIGN_DOC: 'VIEW_ADD_DESIGN_DOC',
diff --git a/app/addons/documents/index-editor/components/DesignDocSelector.js b/app/addons/documents/index-editor/components/DesignDocSelector.js
index 57c6bbc..a524c32 100644
--- a/app/addons/documents/index-editor/components/DesignDocSelector.js
+++ b/app/addons/documents/index-editor/components/DesignDocSelector.js
@@ -21,6 +21,7 @@
constructor(props) {
super(props);
+ this.onTogglePartitioned = this.onTogglePartitioned.bind(this);
}
validate() {
@@ -80,6 +81,39 @@
);
}
+ onTogglePartitioned() {
+ this.props.onChangeNewDesignDocPartitioned(!this.props.newDesignDocPartitioned);
+ }
+
+ getPartitionedCheckbox() {
+ if (!this.props.isDbPartitioned) {
+ return null;
+ }
+ const isExistingDDoc = this.props.selectedDesignDocName !== 'new-doc';
+ const checked = isExistingDDoc ?
+ this.props.selectedDesignDocPartitioned :
+ this.props.newDesignDocPartitioned;
+ const labelClass = isExistingDDoc ? 'check--disabled' : '';
+ const inputTitle = isExistingDDoc ?
+ (this.props.selectedDesignDocPartitioned ? 'Design document is partitioned' : 'Design document is not partitioned') :
+ (this.props.newDesignDocPartitioned ? 'New document will be partitioned' : 'New document will not be partitioned');
+ return (
+ <div className="ddoc-selector-partitioned">
+ <label className={labelClass} title={inputTitle}>
+ <input
+ id="js-ddoc-selector-partitioned"
+ type="checkbox"
+ title={inputTitle}
+ checked={checked}
+ onChange={this.onTogglePartitioned}
+ style={{margin: '0px 10px 0px 0px'}}
+ disabled={isExistingDDoc}/>
+ Partitioned
+ </label>
+ </div>
+ );
+ }
+
render() {
const selectContent =
<optgroup label="Select a document">
@@ -101,6 +135,7 @@
/>
</div>
{this.getNewDDocField()}
+ {this.getPartitionedCheckbox()}
</div>
);
}
@@ -117,7 +152,9 @@
onSelectDesignDoc: PropTypes.func.isRequired,
onChangeNewDesignDocName: PropTypes.func.isRequired,
selectedDesignDocName: PropTypes.string.isRequired,
+ selectedDesignDocPartitioned: PropTypes.bool.isRequired,
newDesignDocName: PropTypes.string.isRequired,
+ newDesignDocPartitioned: PropTypes.bool.isRequired,
designDocLabel: PropTypes.string,
docURL: PropTypes.string
};
diff --git a/app/addons/documents/index-editor/components/IndexEditor.js b/app/addons/documents/index-editor/components/IndexEditor.js
index 332b749..2d1a2cc 100644
--- a/app/addons/documents/index-editor/components/IndexEditor.js
+++ b/app/addons/documents/index-editor/components/IndexEditor.js
@@ -25,6 +25,9 @@
constructor(props) {
super(props);
+ this.saveView = this.saveView.bind(this);
+ this.viewChange = this.viewChange.bind(this);
+ this.updateMapCode = this.updateMapCode.bind(this);
}
// the code editor is a standalone component, so if the user goes from one edit view page to another, we need to
@@ -35,6 +38,21 @@
}
}
+ isPartitionedView() {
+ if (this.props.designDocId === 'new-doc') {
+ return this.props.newDesignDocPartitioned;
+ }
+ return this.props.designDocPartitioned;
+ }
+
+ isCustomReduceSupported() {
+ if (this.props.isDbPartitioned && this.props.reduce && !this.props.reduce.startsWith('_')) {
+ const isDDocPartitioned = this.props.designDocId === 'new-doc' ? this.props.newDesignDocPartitioned : this.props.designDocPartitioned;
+ return isDDocPartitioned ? false : true;
+ }
+ return true;
+ }
+
saveView(el) {
el.preventDefault();
@@ -42,6 +60,18 @@
return;
}
+ if (!this.isCustomReduceSupported()) {
+ FauxtonAPI.addNotification({
+ msg: 'Partitioned views do not support custom reduce functions.',
+ type: 'error',
+ clear: true
+ });
+ return;
+ }
+
+ const encodedPartKey = this.isPartitionedView() && this.props.partitionKey ? encodeURIComponent(this.props.partitionKey) : '';
+ const url = FauxtonAPI.urls('view', 'showView', this.props.database.safeID(), encodedPartKey,
+ this.props.saveDesignDoc.safeID(), encodeURIComponent(this.props.viewName));
this.props.saveView({
database: this.props.database,
isNewView: this.props.isNewView,
@@ -54,7 +84,7 @@
map: this.mapEditor.getValue(),
reduce: this.reduceEditor.getReduceValue(),
designDocs: this.props.designDocs
- });
+ }, url);
}
viewChange(el) {
@@ -65,6 +95,17 @@
this.props.updateMapCode(code);
}
+ getCancelLink() {
+ const encodedDatabase = encodeURIComponent(this.props.database.id);
+ const encodedPartitionKey = this.props.partitionKey ? encodeURIComponent(this.props.partitionKey) : '';
+ const encodedDDoc = encodeURIComponent(this.props.designDocId);
+ const encodedView = encodeURIComponent(this.props.viewName);
+ if (this.props.designDocId === 'new-doc' || this.props.isNewView) {
+ return '#' + FauxtonAPI.urls('allDocs', 'app', encodedDatabase, encodedPartitionKey);
+ }
+ return '#' + FauxtonAPI.urls('view', 'showView', encodedDatabase, encodedPartitionKey, encodedDDoc, encodedView);
+ }
+
render() {
if (this.props.isLoading) {
return (
@@ -76,20 +117,23 @@
const pageHeader = (this.props.isNewView) ? 'New View' : 'Edit View';
const btnLabel = (this.props.isNewView) ? 'Create Document and then Build Index' : 'Save Document and then Build Index';
- const cancelLink = '#' + FauxtonAPI.urls('view', 'showView', this.props.database.id, this.props.designDocId, this.props.viewName);
return (
<div className="define-view" >
- <form className="form-horizontal view-query-save" onSubmit={this.saveView.bind(this)}>
+ <form className="form-horizontal view-query-save" onSubmit={this.saveView}>
<h3 className="simple-header">{pageHeader}</h3>
<div className="new-ddoc-section">
<DesignDocSelector
ref={(el) => { this.designDocSelector = el; }}
designDocList={this.props.designDocList}
+ isDbPartitioned={this.props.isDbPartitioned}
selectedDesignDocName={this.props.designDocId}
+ selectedDesignDocPartitioned={this.props.designDocPartitioned}
newDesignDocName={this.props.newDesignDocName}
+ newDesignDocPartitioned={this.props.newDesignDocPartitioned}
onSelectDesignDoc={this.props.selectDesignDoc}
onChangeNewDesignDocName={this.props.updateNewDesignDocName}
+ onChangeNewDesignDocPartitioned={this.props.updateNewDesignDocPartitioned}
docLink={getDocUrl('DESIGN_DOCS')} />
</div>
@@ -109,7 +153,7 @@
type="text"
id="index-name"
value={this.props.viewName}
- onChange={this.viewChange.bind(this)}
+ onChange={this.viewChange}
placeholder="Index name" />
</div>
<CodeEditorPanel
@@ -117,14 +161,17 @@
ref={(el) => { this.mapEditor = el; }}
title={"Map function"}
docLink={getDocUrl('MAP_FUNCS')}
- blur={this.updateMapCode.bind(this)}
+ blur={this.updateMapCode}
allowZenMode={false}
defaultCode={this.props.map} />
- <ReduceEditor ref={(el) => { this.reduceEditor = el; }} {...this.props} />
+ <ReduceEditor
+ ref={(el) => { this.reduceEditor = el; }}
+ customReducerSupported={this.isCustomReduceSupported()}
+ {...this.props} />
<div className="padded-box">
<div className="control-group">
<ConfirmButton id="save-view" text={btnLabel} />
- <a href={cancelLink} className="index-cancel-link">Cancel</a>
+ <a href={this.getCancelLink()} className="index-cancel-link">Cancel</a>
</div>
</div>
</form>
@@ -137,13 +184,16 @@
isLoading:PropTypes.bool.isRequired,
isNewView: PropTypes.bool.isRequired,
database: PropTypes.object.isRequired,
+ isDbPartitioned: PropTypes.bool.isRequired,
designDocId: PropTypes.string.isRequired,
+ newDesignDocName: PropTypes.string.isRequired,
viewName: PropTypes.string.isRequired,
isNewDesignDoc: PropTypes.bool.isRequired,
originalViewName: PropTypes.string,
originalDesignDocName: PropTypes.string,
designDocs: PropTypes.object,
saveDesignDoc: PropTypes.object,
+ partitionKey: PropTypes.string,
updateNewDesignDocName: PropTypes.func.isRequired,
changeViewName: PropTypes.func.isRequired,
updateMapCode: PropTypes.func.isRequired
diff --git a/app/addons/documents/index-editor/components/IndexEditorContainer.js b/app/addons/documents/index-editor/components/IndexEditorContainer.js
index 445b029..2e7a136 100644
--- a/app/addons/documents/index-editor/components/IndexEditorContainer.js
+++ b/app/addons/documents/index-editor/components/IndexEditorContainer.js
@@ -13,35 +13,41 @@
import { connect } from 'react-redux';
import IndexEditor from './IndexEditor';
import Actions from '../actions';
-import { getSaveDesignDoc, getDesignDocIds, reduceSelectedOption, hasCustomReduce } from '../reducers';
+import { getSaveDesignDoc, getDesignDocList, reduceSelectedOption, hasCustomReduce,
+ getSelectedDesignDocPartitioned } from '../reducers';
-const mapStateToProps = ({ indexEditor }) => {
+const mapStateToProps = ({ indexEditor, databases }, ownProps) => {
+ const isSelectedDDocPartitioned = getSelectedDesignDocPartitioned(indexEditor, databases.isDbPartitioned);
return {
database: indexEditor.database,
isNewView: indexEditor.isNewView,
viewName: indexEditor.viewName,
designDocs: indexEditor.designDocs,
- designDocList: getDesignDocIds(indexEditor),
+ designDocList: getDesignDocList(indexEditor),
originalViewName: indexEditor.originalViewName,
originalDesignDocName: indexEditor.originalDesignDocName,
isNewDesignDoc: indexEditor.isNewDesignDoc,
designDocId: indexEditor.designDocId,
+ designDocPartitioned: isSelectedDDocPartitioned,
newDesignDocName: indexEditor.newDesignDocName,
- saveDesignDoc: getSaveDesignDoc(indexEditor),
+ newDesignDocPartitioned: indexEditor.newDesignDocPartitioned,
+ saveDesignDoc: getSaveDesignDoc(indexEditor, databases.isDbPartitioned),
map: indexEditor.view.map,
isLoading: indexEditor.isLoading,
reduce: indexEditor.view.reduce,
reduceOptions: indexEditor.reduceOptions,
reduceSelectedOption: reduceSelectedOption(indexEditor),
hasCustomReduce: hasCustomReduce(indexEditor),
- hasReduce: !!indexEditor.view.reduce
+ hasReduce: !!indexEditor.view.reduce,
+ isDbPartitioned: databases.isDbPartitioned,
+ partitionKey: ownProps.partitionKey
};
};
const mapDispatchToProps = (dispatch) => {
return {
- saveView: (viewInfo) => {
- dispatch(Actions.saveView(viewInfo));
+ saveView: (viewInfo, navigateToURL) => {
+ dispatch(Actions.saveView(viewInfo, navigateToURL));
},
changeViewName: (name) => {
@@ -60,6 +66,10 @@
dispatch(Actions.updateNewDesignDocName(designDocName));
},
+ updateNewDesignDocPartitioned: (isPartitioned) => {
+ dispatch(Actions.updateNewDesignDocPartitioned(isPartitioned));
+ },
+
updateReduceCode: (code) => {
dispatch(Actions.updateReduceCode(code));
},
diff --git a/app/addons/documents/index-editor/components/ReduceEditor.js b/app/addons/documents/index-editor/components/ReduceEditor.js
index 7f1df52..2520576 100644
--- a/app/addons/documents/index-editor/components/ReduceEditor.js
+++ b/app/addons/documents/index-editor/components/ReduceEditor.js
@@ -58,7 +58,7 @@
const reduceOptions = this.getOptionsList();
let customReduceSection;
- if (this.props.hasCustomReduce) {
+ if (this.props.hasCustomReduce && this.props.customReducerSupported) {
customReduceSection = <CodeEditorPanel
ref={node => this.reduceEditor = node}
id='reduce-function'
@@ -67,6 +67,12 @@
allowZenMode={false}
blur={this.updateReduceCode.bind(this)}
/>;
+ } else if (!this.props.customReducerSupported) {
+ customReduceSection = (
+ <div className="reduce-editor-warning">
+ <label>Partitioned views do not support custom reduce functions.</label>
+ </div>
+ );
}
return (
@@ -96,10 +102,15 @@
}
}
+ReduceEditor.defaultProps = {
+ customReducerSupported: true
+};
+
ReduceEditor.propTypes = {
reduceOptions: PropTypes.array.isRequired,
hasReduce: PropTypes.bool.isRequired,
hasCustomReduce: PropTypes.bool.isRequired,
+ customReducerSupported: PropTypes.bool,
reduce: PropTypes.string,
reduceSelectedOption: PropTypes.string.isRequired,
updateReduceCode: PropTypes.func.isRequired,
diff --git a/app/addons/documents/index-editor/reducers.js b/app/addons/documents/index-editor/reducers.js
index 663818f..51988f6 100644
--- a/app/addons/documents/index-editor/reducers.js
+++ b/app/addons/documents/index-editor/reducers.js
@@ -11,11 +11,13 @@
// the License.
import ActionTypes from './actiontypes';
-import Resources from "../resources";
+import Resources from '../resources';
+import Helpers from '../helpers';
const defaultMap = 'function (doc) {\n emit(doc._id, 1);\n}';
const defaultReduce = 'function (keys, values, rereduce) {\n if (rereduce) {\n return sum(values);\n } else {\n return values.length;\n }\n}';
-const builtInReducers = ['_sum', '_count', '_stats'];
+const builtInReducers = ['_sum', '_count', '_stats', '_approx_count_distinct'];
+const allReducers = builtInReducers.concat(['CUSTOM', 'NONE']);
const initialState = {
designDocs: new Backbone.Collection(),
@@ -25,11 +27,12 @@
designDocId: '',
isNewDesignDoc: false,
newDesignDocName: '',
+ newDesignDocPartitioned: true,
isNewView: false,
viewName: '',
originalViewName: '',
originalDesignDocName: '',
- reduceOptions: builtInReducers.concat(['CUSTOM', 'NONE'])
+ reduceOptions: allReducers
};
function editIndex(state, options) {
@@ -61,6 +64,16 @@
return designDoc.get('views')[state.viewName];
}
+export function getSelectedDesignDocPartitioned(state, isDbPartitioned) {
+ const designDoc = state.designDocs.find(ddoc => {
+ return state.designDocId === ddoc.id;
+ });
+ if (designDoc) {
+ return Helpers.isDDocPartitioned(designDoc.get('doc'), isDbPartitioned);
+ }
+ return false;
+}
+
export function reduceSelectedOption(state) {
if (!state.view.reduce) {
return 'NONE';
@@ -78,14 +91,18 @@
return false;
}
-export function getSaveDesignDoc(state) {
+export function getSaveDesignDoc(state, isDbPartitioned) {
if (state.designDocId === 'new-doc') {
const doc = {
_id: '_design/' + state.newDesignDocName,
views: {},
language: 'javascript'
};
- return new Resources.Doc(doc, { database: state.database });
+ const dDoc = new Resources.Doc(doc, { database: state.database });
+ if (isDbPartitioned) {
+ dDoc.setDDocPartitionedOption(state.newDesignDocPartitioned);
+ }
+ return dDoc;
}
if (!state.designDocs) {
@@ -100,7 +117,7 @@
}
// returns a simple array of design doc IDs. Omits mango docs
-export function getDesignDocIds(state) {
+export function getDesignDocList(state) {
if (!state.designDocs) {
return [];
}
@@ -190,6 +207,12 @@
newDesignDocName: options.value
};
+ case ActionTypes.DESIGN_DOC_NEW_PARTITIONED_UPDATED:
+ return {
+ ...state,
+ newDesignDocPartitioned: options.value
+ };
+
default:
return state;
}
diff --git a/app/addons/documents/layouts.js b/app/addons/documents/layouts.js
index 0d59329..9c39f13 100644
--- a/app/addons/documents/layouts.js
+++ b/app/addons/documents/layouts.js
@@ -249,7 +249,7 @@
dbName, dropDownLinks, selectedNavItem, designDocInfo, partitionKey }) => {
const content = showEditView ?
- <IndexEditorComponents.IndexEditorContainer /> :
+ <IndexEditorComponents.IndexEditorContainer partitionKey={partitionKey}/> :
<DesignDocInfoContainer
designDocInfo={designDocInfo}
designDocName={selectedNavItem.designDocName}/>;
diff --git a/app/addons/documents/mango/components/MangoQueryEditor.js b/app/addons/documents/mango/components/MangoQueryEditor.js
index ecbfea0..a702955 100644
--- a/app/addons/documents/mango/components/MangoQueryEditor.js
+++ b/app/addons/documents/mango/components/MangoQueryEditor.js
@@ -145,8 +145,9 @@
this.props.manageIndexes();
+ const encodedPartKey = this.props.partitionKey ? encodeURIComponent(this.props.partitionKey) : '';
const manageIndexURL = '#' + FauxtonAPI.urls('mango', 'index-app',
- encodeURIComponent(this.props.databaseName), encodeURIComponent(this.props.partitionKey));
+ encodeURIComponent(this.props.databaseName), encodedPartKey);
FauxtonAPI.navigate(manageIndexURL);
}
diff --git a/app/addons/documents/mango/mango.api.js b/app/addons/documents/mango/mango.api.js
index 9655592..59cbebf 100644
--- a/app/addons/documents/mango/mango.api.js
+++ b/app/addons/documents/mango/mango.api.js
@@ -16,7 +16,8 @@
import Constants from '../constants';
export const fetchQueryExplain = (databaseName, partitionKey, queryCode) => {
- const url = FauxtonAPI.urls('mango', 'explain-server', encodeURIComponent(databaseName), encodeURIComponent(partitionKey));
+ const encodedPartKey = partitionKey ? encodeURIComponent(partitionKey) : '';
+ const url = FauxtonAPI.urls('mango', 'explain-server', encodeURIComponent(databaseName), encodedPartKey);
return post(url, queryCode, {rawBody: true}).then((json) => {
if (json.error) {
diff --git a/app/addons/documents/mangolayout.js b/app/addons/documents/mangolayout.js
index 504b7df..70b3e93 100644
--- a/app/addons/documents/mangolayout.js
+++ b/app/addons/documents/mangolayout.js
@@ -165,7 +165,8 @@
let endpoint = this.props.endpoint;
if (this.props.explainPlan) {
- endpoint = FauxtonAPI.urls('mango', 'explain-apiurl', encodeURIComponent(database), encodeURIComponent(partitionKey));
+ const encodedPartKey = partitionKey ? encodeURIComponent(partitionKey) : '';
+ endpoint = FauxtonAPI.urls('mango', 'explain-apiurl', encodeURIComponent(database), encodedPartKey);
}
let queryFunction = (params) => { return MangoAPI.mangoQueryDocs(databaseName, partitionKey, queryFindCode, params); };
let docType = Constants.INDEX_RESULTS_DOC_TYPE.MANGO_QUERY;
diff --git a/app/addons/documents/routes-mango.js b/app/addons/documents/routes-mango.js
index 4e555d6..b4981f0 100644
--- a/app/addons/documents/routes-mango.js
+++ b/app/addons/documents/routes-mango.js
@@ -57,7 +57,7 @@
'allDocs', 'app', encodeURIComponent(this.databaseName), encodedPartitionKey
);
- const partKeyUrlComponent = partitionKey ? `/${encodeURIComponent(partitionKey)}` : '';
+ const partKeyUrlComponent = partitionKey ? `/${encodedPartitionKey}` : '';
const fetchUrl = '/' + encodeURIComponent(this.databaseName) + partKeyUrlComponent + '/_find';
const crumbs = [
diff --git a/app/addons/documents/shared-resources.js b/app/addons/documents/shared-resources.js
index ac5c0e0..d0ff692 100644
--- a/app/addons/documents/shared-resources.js
+++ b/app/addons/documents/shared-resources.js
@@ -100,6 +100,20 @@
return this.docType() === "design doc";
},
+ setDDocPartitionedOption: function (isPartitioned) {
+ if (!this.isDdoc()) {
+ return false;
+ }
+ let options = this.get('options');
+ if (!options) {
+ options = {};
+ }
+ options.partitioned = isPartitioned;
+ this.set({ options });
+
+ return true;
+ },
+
setDdocView: function (view, map, reduce) {
if (!this.isDdoc()) {
return false;
diff --git a/app/addons/documents/sidebar/SidebarControllerContainer.js b/app/addons/documents/sidebar/SidebarControllerContainer.js
index 550f3b7..9b85a26 100644
--- a/app/addons/documents/sidebar/SidebarControllerContainer.js
+++ b/app/addons/documents/sidebar/SidebarControllerContainer.js
@@ -13,7 +13,7 @@
import { connect } from 'react-redux';
import SidebarComponents from './sidebar';
import Action from './actions';
-import { getDatabase } from './reducers';
+import { getDatabase, getDesignDocPartitioned } from './reducers';
// returns a simple array of design doc IDs
@@ -69,7 +69,9 @@
cloneIndexModalVisible: sidebar.cloneIndexModalVisible,
cloneIndexModalTitle: sidebar.cloneIndexModalTitle,
cloneIndexModalSelectedDesignDoc: sidebar.cloneIndexModalSelectedDesignDoc,
+ cloneIndexModalSelectedDesignDocPartitioned: getDesignDocPartitioned(sidebar, databases.isDbPartitioned),
cloneIndexModalNewDesignDocName: sidebar.cloneIndexModalNewDesignDocName,
+ cloneIndexModalNewDesignDocPartitioned: sidebar.cloneIndexModalNewDesignDocPartitioned,
cloneIndexModalOnSubmit: sidebar.cloneIndexModalOnSubmit,
cloneIndexDesignDocProp: sidebar.cloneIndexDesignDocProp,
cloneIndexModalNewIndexName: sidebar.cloneIndexModalNewIndexName,
@@ -102,6 +104,9 @@
updateNewDesignDocName: (designDocName) => {
dispatch(Action.updateNewDesignDocName(designDocName));
},
+ updateNewDesignDocPartitioned: (isPartitioned) => {
+ dispatch(Action.updateNewDesignDocPartitioned(isPartitioned));
+ },
setNewCloneIndexName: (indexName) => {
dispatch(Action.setNewCloneIndexName(indexName));
}
diff --git a/app/addons/documents/sidebar/actions.js b/app/addons/documents/sidebar/actions.js
index 7564b3c..d8ba4cd 100644
--- a/app/addons/documents/sidebar/actions.js
+++ b/app/addons/documents/sidebar/actions.js
@@ -131,6 +131,15 @@
});
};
+const updateNewDesignDocPartitioned = (isPartitioned) => (dispatch) => {
+ dispatch({
+ type: ActionTypes.SIDEBAR_CLONE_MODAL_DESIGN_DOC_NEW_PARTITIONED_UPDATED,
+ options: {
+ value: isPartitioned
+ }
+ });
+};
+
const selectDesignDoc = (designDoc) => (dispatch) => {
dispatch({
type: ActionTypes.SIDEBAR_CLONE_MODAL_DESIGN_DOC_CHANGE,
@@ -159,6 +168,7 @@
showCloneIndexModal,
hideCloneIndexModal,
updateNewDesignDocName,
+ updateNewDesignDocPartitioned,
selectDesignDoc,
setNewCloneIndexName,
dispatchExpandSelectedItem
diff --git a/app/addons/documents/sidebar/actiontypes.js b/app/addons/documents/sidebar/actiontypes.js
index 666b8a9..28c84fc 100644
--- a/app/addons/documents/sidebar/actiontypes.js
+++ b/app/addons/documents/sidebar/actiontypes.js
@@ -21,6 +21,7 @@
SIDEBAR_HIDE_CLONE_INDEX_MODAL: 'SIDEBAR_HIDE_CLONE_INDEX_MODAL',
SIDEBAR_CLONE_MODAL_DESIGN_DOC_CHANGE: 'SIDEBAR_CLONE_MODAL_DESIGN_DOC_CHANGE',
SIDEBAR_CLONE_MODAL_DESIGN_DOC_NEW_NAME_UPDATED: 'SIDEBAR_CLONE_MODAL_DESIGN_DOC_NEW_NAME_UPDATED',
+ SIDEBAR_CLONE_MODAL_DESIGN_DOC_NEW_PARTITIONED_UPDATED: 'SIDEBAR_CLONE_MODAL_DESIGN_DOC_NEW_PARTITIONED_UPDATED',
SIDEBAR_CLONE_MODAL_UPDATE_INDEX_NAME: 'SIDEBAR_CLONE_MODAL_UPDATE_INDEX_NAME',
SIDEBAR_UPDATED_DESIGN_DOCS: 'SIDEBAR_UPDATED_DESIGN_DOCS'
};
diff --git a/app/addons/documents/sidebar/components/CloneIndexModal.js b/app/addons/documents/sidebar/components/CloneIndexModal.js
index 220e8ab..ca83562 100644
--- a/app/addons/documents/sidebar/components/CloneIndexModal.js
+++ b/app/addons/documents/sidebar/components/CloneIndexModal.js
@@ -89,10 +89,14 @@
<DesignDocSelector
ref={node => this.designDocSelector = node}
designDocList={this.props.designDocArray}
+ isDbPartitioned={this.props.isDbPartitioned}
selectedDesignDocName={this.props.selectedDesignDoc}
+ selectedDesignDocPartitioned={this.props.selectedDesignDocPartitioned}
newDesignDocName={this.props.newDesignDocName}
+ newDesignDocPartitioned={this.props.newDesignDocPartitioned}
onSelectDesignDoc={this.props.selectDesignDoc}
- onChangeNewDesignDocName={this.props.updateNewDesignDocName} />
+ onChangeNewDesignDocName={this.props.updateNewDesignDocName}
+ onChangeNewDesignDocPartitioned={this.props.updateNewDesignDocPartitioned} />
</div>
<div className="clone-index-name-row">
diff --git a/app/addons/documents/sidebar/components/DesignDocList.js b/app/addons/documents/sidebar/components/DesignDocList.js
index 2113470..c520659 100644
--- a/app/addons/documents/sidebar/components/DesignDocList.js
+++ b/app/addons/documents/sidebar/components/DesignDocList.js
@@ -12,8 +12,8 @@
import PropTypes from 'prop-types';
import React from 'react';
-import ReactDOM from 'react-dom';
import FauxtonAPI from '../../../../core/api';
+import DocHelpers from '../../helpers';
import DesignDoc from './DesignDoc';
export default class DesignDocList extends React.Component {
@@ -43,12 +43,7 @@
designDocList = () => {
return _.map(this.props.designDocs, (designDoc, key) => {
const ddName = decodeURIComponent(designDoc.safeId);
- // By default a design doc is partitioned if the database is partitioned
- let isDDocPartitioned = this.props.isDbPartitioned;
- // Check if it is explictly set to not partitioned
- if (this.props.isDbPartitioned && designDoc.options && designDoc.options.partitioned === false) {
- isDDocPartitioned = false;
- }
+ const isDDocPartitioned = DocHelpers.isDDocPartitioned(designDoc, this.props.isDbPartitioned);
// only pass down the selected nav info and toggle info if they're relevant for this particular design doc
let expanded = false,
diff --git a/app/addons/documents/sidebar/components/SidebarController.js b/app/addons/documents/sidebar/components/SidebarController.js
index b37c8ac..bda4bfa 100644
--- a/app/addons/documents/sidebar/components/SidebarController.js
+++ b/app/addons/documents/sidebar/components/SidebarController.js
@@ -12,7 +12,6 @@
import PropTypes from 'prop-types';
import React from 'react';
-import ReactDOM from 'react-dom';
import ComponentsActions from "../../../components/actions";
import Components from '../../../components/react-components';
import ComponentsStore from '../../../components/stores';
@@ -91,6 +90,8 @@
sourceDesignDocName: this.props.cloneIndexSourceDesignDocName,
targetDesignDocName: this.props.cloneIndexModalSelectedDesignDoc,
newDesignDocName: this.props.cloneIndexModalNewDesignDocName,
+ newDesignDocPartitioned: this.props.cloneIndexModalNewDesignDocPartitioned,
+ isDbPartitioned: this.props.isDbPartitioned,
newIndexName: this.props.cloneIndexModalNewIndexName,
designDocs: this.props.designDocs,
database: this.props.database,
@@ -137,12 +138,16 @@
submit={this.cloneIndex}
designDocArray={this.props.availableDesignDocIds}
selectedDesignDoc={this.props.cloneIndexModalSelectedDesignDoc}
+ selectedDesignDocPartitioned={this.props.cloneIndexModalSelectedDesignDocPartitioned}
newDesignDocName={this.props.cloneIndexModalNewDesignDocName}
+ newDesignDocPartitioned={this.props.cloneIndexModalNewDesignDocPartitioned}
newIndexName={this.props.cloneIndexModalNewIndexName}
indexLabel={this.props.cloneIndexModalIndexLabel}
selectDesignDoc={this.props.selectDesignDoc}
updateNewDesignDocName={this.props.updateNewDesignDocName}
- setNewCloneIndexName={this.props.setNewCloneIndexName} />
+ updateNewDesignDocPartitioned={this.props.updateNewDesignDocPartitioned}
+ setNewCloneIndexName={this.props.setNewCloneIndexName}
+ isDbPartitioned={this.props.isDbPartitioned} />
</nav>
);
}
diff --git a/app/addons/documents/sidebar/reducers.js b/app/addons/documents/sidebar/reducers.js
index f1fa140..a5491d8 100644
--- a/app/addons/documents/sidebar/reducers.js
+++ b/app/addons/documents/sidebar/reducers.js
@@ -10,9 +10,10 @@
// License for the specific language governing permissions and limitations under
// the License.
-import React from "react";
-import app from "../../../app";
-import ActionTypes from "./actiontypes";
+import React from 'react';
+import app from '../../../app';
+import Helpers from '../helpers';
+import ActionTypes from './actiontypes';
const initialState = {
designDocs: new Backbone.Collection(),
@@ -37,6 +38,7 @@
cloneIndexModalTitle: '',
cloneIndexModalSelectedDesignDoc: '',
cloneIndexModalNewDesignDocName: '',
+ cloneIndexModalNewDesignDocPartitioned: true,
cloneIndexModalNewIndexName: '',
cloneIndexModalSourceIndexName: '',
cloneIndexModalSourceDesignDocName: '',
@@ -136,6 +138,16 @@
return ddocsList;
}
+export function getDesignDocPartitioned(state, isDbPartitioned) {
+ const designDoc = state.designDocs.find(ddoc => {
+ return state.cloneIndexModalSelectedDesignDoc == ddoc.id;
+ });
+ if (designDoc) {
+ return Helpers.isDDocPartitioned(designDoc.get('doc'), isDbPartitioned);
+ }
+ return false;
+}
+
export const getDatabase = (state) => {
if (state.loading) {
return {};
@@ -214,6 +226,12 @@
cloneIndexModalNewDesignDocName: options.value
};
+ case ActionTypes.SIDEBAR_CLONE_MODAL_DESIGN_DOC_NEW_PARTITIONED_UPDATED:
+ return {
+ ...state,
+ cloneIndexModalNewDesignDocPartitioned: options.value
+ };
+
case ActionTypes.SIDEBAR_CLONE_MODAL_UPDATE_INDEX_NAME:
return {
...state,
diff --git a/app/addons/documents/tests/nightwatch/viewCreateBadView.js b/app/addons/documents/tests/nightwatch/viewCreateBadView.js
index 66eb74a..7405ed0 100644
--- a/app/addons/documents/tests/nightwatch/viewCreateBadView.js
+++ b/app/addons/documents/tests/nightwatch/viewCreateBadView.js
@@ -31,7 +31,7 @@
.clearValue('#index-name')
.setValue('#index-name', 'hasenindex')
.clickWhenVisible('#reduce-function-selector')
- .keys(['\uE013', '\uE013', '\uE013', '\uE013', '\uE006'])
+ .keys(['\uE013', '\uE013', '\uE013', '\uE013', '\uE013', '\uE006'])
.execute('\
var editor = ace.edit("map-function");\
editor.getSession().setValue("function (doc) { emit(\'boom\', doc._id); }");\
diff --git a/app/addons/documents/tests/nightwatch/viewEdit.js b/app/addons/documents/tests/nightwatch/viewEdit.js
index 6214a8e..6a1dd41 100644
--- a/app/addons/documents/tests/nightwatch/viewEdit.js
+++ b/app/addons/documents/tests/nightwatch/viewEdit.js
@@ -128,7 +128,7 @@
.clearValue('#index-name')
.setValue('#index-name', 'view1')
.clickWhenVisible('#reduce-function-selector')
- .keys(['\uE013', '\uE013', '\uE013', '\uE013', '\uE006'])
+ .keys(['\uE013', '\uE013', '\uE013', '\uE013', '\uE013', '\uE006'])
.execute('\
var editor = ace.edit("map-function");\
editor.getSession().setValue("function (doc) { emit(doc._id, 100); }");\