You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by am...@apache.org on 2019/03/14 18:28:59 UTC

[couchdb-fauxton] branch master updated: Disable stable query option for partitioned views (#1193)

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 c8d938f  Disable stable query option for partitioned views (#1193)
c8d938f is described below

commit c8d938fc8c5ad1751f4b29286804acd821c57485
Author: Antonio Maranhao <30...@users.noreply.github.com>
AuthorDate: Thu Mar 14 14:28:55 2019 -0400

    Disable stable query option for partitioned views (#1193)
---
 app/addons/documents/__tests__/helpers.test.js     | 78 +++++++++++++++++++
 .../documents/__tests__/main-fields-view.test.js   |  4 +-
 .../documents/__tests__/query-options.test.js      | 56 ++++++++++++-
 .../documents/assets/less/query-options.less       |  3 +-
 app/addons/documents/helpers.js                    | 17 ++++
 .../components/queryoptions/MainFieldsView.js      | 91 ++++++++++++++--------
 .../components/queryoptions/QueryOptions.js        |  1 +
 .../containers/QueryOptionsContainer.js            | 11 ++-
 8 files changed, 222 insertions(+), 39 deletions(-)

diff --git a/app/addons/documents/__tests__/helpers.test.js b/app/addons/documents/__tests__/helpers.test.js
index 195584f..d586f34 100644
--- a/app/addons/documents/__tests__/helpers.test.js
+++ b/app/addons/documents/__tests__/helpers.test.js
@@ -42,4 +42,82 @@ describe('Helpers', () => {
 
   });
 
+  describe('selectedItemIsPartitionedView', () => {
+    const ddocs = {
+      find: () => { return {_id: '_design/ddoc1' }; }
+    };
+    const selectedView = {
+      navItem: 'designDoc',
+      designDocSection: 'Views',
+      indexName: 'view1'
+    };
+    const selectedAllDocs = {
+      navItem: 'all-docs'
+    };
+
+    it('returns false if no item is selected', () => {
+      const isPartitionedView = Helpers.selectedItemIsPartitionedView(ddocs, null, true);
+      expect(isPartitionedView).toBe(false);
+    });
+
+    it('returns false if selected item is not a view', () => {
+      const isPartitionedView = Helpers.selectedItemIsPartitionedView(ddocs, selectedAllDocs, true);
+      expect(isPartitionedView).toBe(false);
+    });
+
+    it('returns false if selected item is a global view', () => {
+      const isPartitionedView = Helpers.selectedItemIsPartitionedView(ddocs, selectedView, false);
+      expect(isPartitionedView).toBe(false);
+    });
+
+    it('returns true if selected item is a partitioned view', () => {
+      const isPartitionedView = Helpers.selectedItemIsPartitionedView(ddocs, selectedView, true);
+      expect(isPartitionedView).toBe(true);
+    });
+
+  });
+
+  describe('isDDocPartitioned', () => {
+    const ddocNoOptions = {
+      _id: '_design/ddoc1'
+    };
+    const ddocPartitionedTrue = {
+      _id: '_design/ddoc1',
+      options: {
+        partitioned: true
+      }
+    };
+    const ddocPartitionedFalse = {
+      _id: '_design/ddoc1',
+      options: {
+        partitioned: false
+      }
+    };
+
+    it('returns false if database is not partitioned', () => {
+      let isPartitionedDdoc = Helpers.isDDocPartitioned(ddocNoOptions, false);
+      expect(isPartitionedDdoc).toBe(false);
+
+      isPartitionedDdoc = Helpers.isDDocPartitioned(ddocPartitionedFalse, false);
+      expect(isPartitionedDdoc).toBe(false);
+
+      isPartitionedDdoc = Helpers.isDDocPartitioned(ddocPartitionedTrue, false);
+      expect(isPartitionedDdoc).toBe(false);
+    });
+
+    it('returns true if database is partitioned and ddoc partitioned option is not set to false', () => {
+      let isPartitionedDdoc = Helpers.isDDocPartitioned(ddocNoOptions, true);
+      expect(isPartitionedDdoc).toBe(true);
+
+      isPartitionedDdoc = Helpers.isDDocPartitioned(ddocPartitionedTrue, true);
+      expect(isPartitionedDdoc).toBe(true);
+    });
+
+    it('returns false if database is partitioned but ddoc is set as non-partitioned', () => {
+      const isPartitionedDdoc = Helpers.isDDocPartitioned(ddocPartitionedFalse, true);
+      expect(isPartitionedDdoc).toBe(false);
+    });
+
+  });
+
 });
diff --git a/app/addons/documents/__tests__/main-fields-view.test.js b/app/addons/documents/__tests__/main-fields-view.test.js
index fe108ca..3a5b960 100644
--- a/app/addons/documents/__tests__/main-fields-view.test.js
+++ b/app/addons/documents/__tests__/main-fields-view.test.js
@@ -21,7 +21,8 @@ describe('MainFieldsView', () => {
     stable: false,
     toggleStable: () => {},
     update: 'true',
-    changeUpdateField: () => {}
+    changeUpdateField: () => {},
+    enableStable: false
   };
   const docURL = 'http://foo.com';
   it('does not render reduce when showReduce is false', () => {
@@ -110,6 +111,7 @@ describe('MainFieldsView', () => {
       toggleIncludeDocs={() => {}}
       toggleStable={spy}
       docURL={docURL}
+      enableStable={true}
     />);
 
     wrapper.find('#qoStable').simulate('change');
diff --git a/app/addons/documents/__tests__/query-options.test.js b/app/addons/documents/__tests__/query-options.test.js
index 90d5256..4459ba0 100644
--- a/app/addons/documents/__tests__/query-options.test.js
+++ b/app/addons/documents/__tests__/query-options.test.js
@@ -10,11 +10,10 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+import { shallow, mount } from 'enzyme';
 import React from 'react';
-import ReactDOM from 'react-dom';
-import { shallow } from 'enzyme';
-import QueryOptions from '../index-results/components/queryoptions/QueryOptions';
 import sinon from 'sinon';
+import QueryOptions from '../index-results/components/queryoptions/QueryOptions';
 import Constants from '../constants';
 
 describe('QueryOptions', () => {
@@ -27,7 +26,10 @@ describe('QueryOptions', () => {
     queryOptionsToggleStable: () => {},
     queryOptionsChangeUpdate: () => {},
     stable: false,
-    update: 'true'
+    update: 'true',
+    betweenKeys: {},
+    showReduce: true,
+    enableStable: true
   };
 
   it('calls resetPagination and queryOptionsExecute on submit', () => {
@@ -300,4 +302,50 @@ describe('QueryOptions', () => {
     const isHighlighted = wrapper.find('ToggleHeaderButton').prop('active');
     expect(isHighlighted).toBe(false);
   });
+
+  it('stable option is only enabled when enableStable is true', () => {
+    const wrapper = mount(<QueryOptions
+      {...props}
+      ddocsOnly={true}
+      update='true'
+      queryOptionsRemoveFilterOnlyDdocs={() => {}}
+      queryOptionsApplyFilterOnlyDdocs={() => {}}
+      queryOptionsExecute={() => {}}
+      resetPagination={() => {}}
+      queryOptionsToggleVisibility={() => {}}
+      queryOptionsParams={{}}
+      selectedLayout={Constants.LAYOUT_ORIENTATION.METADATA}
+      changeLayout={() => {}}
+      showReduce={true}
+      enableStable={true}
+    />);
+
+    expect(wrapper.find('input#qoStable').prop("disabled")).toBe(false);
+    wrapper.setProps({enableStable: false});
+    expect(wrapper.find('input#qoStable').prop("disabled")).toBe(true);
+  });
+
+  it('reduce option is only displayed when showReduce is true', () => {
+    const wrapper = mount(<QueryOptions
+      {...props}
+      ddocsOnly={true}
+      update='true'
+      queryOptionsRemoveFilterOnlyDdocs={() => {}}
+      queryOptionsApplyFilterOnlyDdocs={() => {}}
+      queryOptionsExecute={() => {}}
+      resetPagination={() => {}}
+      queryOptionsToggleVisibility={() => {}}
+      queryOptionsParams={{}}
+      selectedLayout={Constants.LAYOUT_ORIENTATION.METADATA}
+      changeLayout={() => {}}
+      showReduce={true}
+      enableStable={true}
+    />);
+
+    expect(wrapper.find('input#qoReduce').exists()).toBe(true);
+
+    wrapper.setProps({showReduce: false});
+    expect(wrapper.find('input#qoReduce').exists()).toBe(false);
+  });
+
 });
diff --git a/app/addons/documents/assets/less/query-options.less b/app/addons/documents/assets/less/query-options.less
index 27e9cc0..f2ace1a 100644
--- a/app/addons/documents/assets/less/query-options.less
+++ b/app/addons/documents/assets/less/query-options.less
@@ -130,7 +130,8 @@
     -webkit-user-select: none;
   }
   label.disabled {
-    color: #777777;
+    cursor: not-allowed;
+    opacity: .5;
   }
   div.controls-group.well{
     height: 156px;
diff --git a/app/addons/documents/helpers.js b/app/addons/documents/helpers.js
index c438969..27623a9 100644
--- a/app/addons/documents/helpers.js
+++ b/app/addons/documents/helpers.js
@@ -122,6 +122,22 @@ const selectedViewContainsReduceFunction = (designDocs, selectedNavItem) => {
   return showReduce;
 };
 
+const selectedItemIsPartitionedView = (designDocs, selectedNavItem, isDbPartitioned) => {
+  if (!selectedNavItem) {
+    return false;
+  }
+
+  let isPartitioned = false;
+  if (designDocs && isViewSelected(selectedNavItem)) {
+    const ddocID = '_design/' + selectedNavItem.designDocName;
+    const ddoc = designDocs.find(ddoc => ddoc._id === ddocID);
+    if (ddoc) {
+      isPartitioned = isDDocPartitioned(ddoc, isDbPartitioned);
+    }
+  }
+  return isPartitioned;
+};
+
 const isViewSelected = (selectedNavItem) => {
   return (selectedNavItem.navItem === 'designDoc'
     && selectedNavItem.designDocSection === 'Views'
@@ -147,5 +163,6 @@ export default {
   parseJSON,
   truncateDoc,
   selectedViewContainsReduceFunction,
+  selectedItemIsPartitionedView,
   isViewSelected
 };
diff --git a/app/addons/documents/index-results/components/queryoptions/MainFieldsView.js b/app/addons/documents/index-results/components/queryoptions/MainFieldsView.js
index f36f652..179acf7 100644
--- a/app/addons/documents/index-results/components/queryoptions/MainFieldsView.js
+++ b/app/addons/documents/index-results/components/queryoptions/MainFieldsView.js
@@ -18,6 +18,7 @@ export default class MainFieldsView extends React.Component {
     super(props);
     this.toggleStable = this.toggleStable.bind(this);
     this.onUpdateChange = this.onUpdateChange.bind(this);
+    this.toggleIncludeDocs = this.toggleIncludeDocs.bind(this);
 
     this.updateOptions = [
       {value: 'true', label: 'true'},
@@ -72,8 +73,21 @@ export default class MainFieldsView extends React.Component {
     this.props.toggleStable(this.props.stable);
   }
 
-  reduce() {
-    if (!this.props.showReduce) {
+  includeDocsOption() {
+    const {includeDocs, reduce} = this.props;
+    return (
+      <div className="checkbox inline">
+        <input disabled={reduce} onChange={this.toggleIncludeDocs} id="qoIncludeDocs"
+          name="include_docs" type="checkbox" checked={includeDocs}/>
+        <label className={reduce ? 'disabled' : ''} htmlFor="qoIncludeDocs" id="qoIncludeDocsLabel">Include
+            Docs</label>
+      </div>
+    );
+  }
+
+  reduceOption() {
+    const {showReduce, reduce} = this.props;
+    if (!showReduce) {
       return null;
     }
 
@@ -81,7 +95,7 @@ export default class MainFieldsView extends React.Component {
       <span>
         <div className="checkbox inline">
           <input id="qoReduce" name="reduce" onChange={this.toggleReduce.bind(this)} type="checkbox"
-            checked={this.props.reduce}/>
+            checked={reduce}/>
           <label htmlFor="qoReduce">Reduce</label>
         </div>
         {this.groupLevel()}
@@ -89,12 +103,41 @@ export default class MainFieldsView extends React.Component {
     );
   }
 
-  getUpdateOptions() {
-    return this.updateOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>);
+  stableOption() {
+    let {enableStable, stable} = this.props;
+
+    if (!enableStable) {
+      // makes sure Stable option always appears unchecked when disabled
+      stable = false;
+    }
+
+    return (
+      <div className="checkbox inline">
+        <input onChange={this.toggleStable} id="qoStable" name="stable"
+          type="checkbox" checked={stable} disabled={!enableStable}/>
+        <label className={enableStable ? '' : 'disabled'} htmlFor="qoStable" id="qoStableLabel">Stable</label>
+      </div>
+    );
+  }
+
+  updateOption() {
+    const { update } = this.props;
+    const selectOptions = this.updateOptions.map(option => {
+      return <option key={option.value} value={option.value}>{option.label}</option>;
+    });
+    return (
+      <div className="dropdown inline">
+        <label className="drop-down">
+          Update
+          <select className="input-small" id="qoUpdate" value={update} onChange={this.onUpdateChange}>
+            {selectOptions}
+          </select>
+        </label>
+      </div>
+    );
   }
 
   render() {
-    let {includeDocs, stable, update} = this.props;
     return (
       <div className="query-group" id="query-options-main-fields">
         <span className="add-on">
@@ -103,31 +146,13 @@ export default class MainFieldsView extends React.Component {
             <i className="icon-question-sign"/>
           </a>
         </span>
-        <div className="controls-group qo-main-fields-row">
-          <div className="row-fluid fieldsets">
-            <div className="checkbox inline">
-              <input disabled={this.props.reduce} onChange={this.toggleIncludeDocs.bind(this)} id="qoIncludeDocs"
-                name="include_docs" type="checkbox" checked={includeDocs}/>
-              <label className={this.props.reduce ? 'disabled' : ''} htmlFor="qoIncludeDocs" id="qoIncludeDocsLabel">Include
-                  Docs</label>
-            </div>
-            {this.reduce()}
-          </div>
-          <div className="row-fluid fieldsets">
-            <div className="checkbox inline">
-              <input onChange={this.toggleStable} id="qoStable"
-                name="include_docs" type="checkbox" checked={stable}/>
-              <label htmlFor="qoStable" id="qoStableLabel">Stable</label>
-            </div>
-            <div className="dropdown inline">
-              <label className="drop-down">
-                Update
-                <select className="input-small" id="qoUpdate" value={update} onChange={this.onUpdateChange}>
-                  {this.getUpdateOptions()}
-                </select>
-              </label>
-            </div>
-          </div>
+        <div className="row-fluid fieldsets">
+          {this.includeDocsOption()}
+          {this.reduceOption()}
+        </div>
+        <div className="row-fluid fieldsets">
+          {this.stableOption()}
+          {this.updateOption()}
         </div>
       </div>
     );
@@ -145,5 +170,7 @@ MainFieldsView.propTypes = {
   stable: PropTypes.bool.isRequired,
   toggleStable: PropTypes.func.isRequired,
   update: PropTypes.string.isRequired,
-  changeUpdateField: PropTypes.func.isRequired
+  changeUpdateField: PropTypes.func.isRequired,
+  showReduce: PropTypes.bool.isRequired,
+  enableStable: PropTypes.bool.isRequired
 };
diff --git a/app/addons/documents/index-results/components/queryoptions/QueryOptions.js b/app/addons/documents/index-results/components/queryoptions/QueryOptions.js
index bbcab32..9b36447 100644
--- a/app/addons/documents/index-results/components/queryoptions/QueryOptions.js
+++ b/app/addons/documents/index-results/components/queryoptions/QueryOptions.js
@@ -113,6 +113,7 @@ export default class QueryOptions extends React.Component {
             groupLevel={this.props.groupLevel}
             updateGroupLevel={this.props.queryOptionsUpdateGroupLevel}
             docURL={FauxtonAPI.constants.DOC_URLS.GENERAL}
+            enableStable={this.props.enableStable}
             stable={this.props.stable}
             toggleStable={this.props.queryOptionsToggleStable}
             update={this.props.update}
diff --git a/app/addons/documents/index-results/containers/QueryOptionsContainer.js b/app/addons/documents/index-results/containers/QueryOptionsContainer.js
index 344e9e4..2c1d4fa 100644
--- a/app/addons/documents/index-results/containers/QueryOptionsContainer.js
+++ b/app/addons/documents/index-results/containers/QueryOptionsContainer.js
@@ -46,7 +46,15 @@ const showReduce = (designDocs, selectedNavItem) => {
   return DocHelpers.selectedViewContainsReduceFunction(designDocs, selectedNavItem);
 };
 
-const mapStateToProps = ({indexResults, sidebar}, ownProps) => {
+const enableStable = (designDocs, selectedNavItem, isDbPartitioned) => {
+  if (DocHelpers.isViewSelected(selectedNavItem)) {
+    const enableStable = !DocHelpers.selectedItemIsPartitionedView(designDocs, selectedNavItem, isDbPartitioned);
+    return enableStable;
+  }
+  return true;
+};
+
+const mapStateToProps = ({indexResults, sidebar, databases}, ownProps) => {
   const queryOptionsPanel = getQueryOptionsPanel(indexResults);
   return {
     contentVisible: queryOptionsPanel.isVisible,
@@ -61,6 +69,7 @@ const mapStateToProps = ({indexResults, sidebar}, ownProps) => {
     descending: queryOptionsPanel.descending,
     skip: queryOptionsPanel.skip,
     limit: queryOptionsPanel.limit,
+    enableStable: enableStable(sidebar.designDocList, ownProps.selectedNavItem, databases.isDbPartitioned),
     stable: queryOptionsPanel.stable,
     update: queryOptionsPanel.update,
     fetchParams: getFetchParams(indexResults),