Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 98E71200D14 for ; Tue, 3 Oct 2017 12:11:12 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 9743E1609DD; Tue, 3 Oct 2017 10:11:12 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id AD9531609D2 for ; Tue, 3 Oct 2017 12:11:08 +0200 (CEST) Received: (qmail 62539 invoked by uid 500); 3 Oct 2017 10:11:07 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 62517 invoked by uid 99); 3 Oct 2017 10:11:07 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 03 Oct 2017 10:11:07 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 7FB49816CC; Tue, 3 Oct 2017 10:11:05 +0000 (UTC) Date: Tue, 03 Oct 2017 10:11:04 +0000 To: "commits@couchdb.apache.org" Subject: [couchdb-fauxton] branch master updated: Refactor map/reduce views to use Redux (#986) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <150702546494.23039.9364434976616323647@gitbox.apache.org> From: garren@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: couchdb-fauxton X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: 838f5342db2e621f6ee138efeae0e2c531623245 X-Git-Newrev: 3edbe4057807d2789655c3c0a3d51fd0bdaba0f2 X-Git-Rev: 3edbe4057807d2789655c3c0a3d51fd0bdaba0f2 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated archived-at: Tue, 03 Oct 2017 10:11:12 -0000 This is an automated email from the ASF dual-hosted git repository. garren 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 3edbe40 Refactor map/reduce views to use Redux (#986) 3edbe40 is described below commit 3edbe4057807d2789655c3c0a3d51fd0bdaba0f2 Author: Antonio Maranhao <30349380+Antonio-Maranhao@users.noreply.github.com> AuthorDate: Tue Oct 3 06:11:02 2017 -0400 Refactor map/reduce views to use Redux (#986) * Use redux IndexResults component for views * Allow disabling bulk delete for IndexResults * Fix test case * Bug fix to avoid sending empty 'keys' query param * Enable reduce query option based on sidebar selection * Remove unused code * Bug fixes * Remove unused code * Highlight Query Options header if an option is active * Bug fix when deleting view * Bug fix when creating a new view in a new design doc * Fix condition for when Query Options header should be active * Make test condition more robust * Bug fix when saving view into another design doc * Fix condition to find if view contains a reduce function * Split index-editor components to separate files and small fixes * Fix nightwatch test * Address Garren's comment * Forgot to commit components.js file * Increase buffer size to avoid failure of Grunt nightwatch task * Show error message when save view operation fails --- Gruntfile.js | 5 +- .../components/components/toggleheaderbutton.js | 4 +- .../documents/__tests__/index-results.test.js | 44 +- .../documents/__tests__/query-options.test.js | 197 ++++- app/addons/documents/__tests__/resources.test.js | 227 +----- .../documents/__tests__/results-toolbar.test.js | 3 +- app/addons/documents/base.js | 4 +- .../documents/components/header-docs-right.js | 29 +- app/addons/documents/header/header.actions.js | 45 -- app/addons/documents/header/header.js | 56 +- app/addons/documents/helpers.js | 28 +- app/addons/documents/index-editor/actions.js | 8 +- app/addons/documents/index-editor/components.js | 365 +-------- .../index-editor/components/DesignDocSelector.js | 121 +++ .../index-editor/components/IndexEditor.js | 167 ++++ .../index-editor/components/ReduceEditor.js | 123 +++ .../index-editor/tests/viewIndex.componentsSpec.js | 57 +- app/addons/documents/index-results/actions.js | 233 ------ .../index-results/actions/queryoptions.js | 22 +- app/addons/documents/index-results/api.js | 33 + .../components/queryoptions/MainFieldsView.js | 6 +- .../components/queryoptions/QueryOptions.js | 45 +- .../components/results/IndexResults.js | 16 +- .../index-results/containers/ApiBarContainer.js | 14 +- .../containers/IndexResultsContainer.js | 2 +- .../containers/QueryOptionsContainer.js | 17 +- .../documents/index-results/helpers/json-view.js | 6 +- .../index-results/helpers/shared-helpers.js | 2 +- .../documents/index-results/helpers/table-view.js | 4 +- .../index-results/index-results.components.js | 486 ------------ app/addons/documents/index-results/reducers.js | 26 +- app/addons/documents/index-results/stores.js | 865 --------------------- .../tests/index-results.actionsSpec.js | 112 --- .../tests/index-results.componentsSpec.js | 330 -------- .../tests/index-results.storesSpec.js | 722 ----------------- app/addons/documents/layouts.js | 104 +-- app/addons/documents/mangolayout.js | 2 +- app/addons/documents/pagination/actions.js | 123 --- app/addons/documents/pagination/actiontypes.js | 23 - app/addons/documents/pagination/pagination.js | 275 ------- .../pagination/tests/pagination.componentSpec.js | 121 --- app/addons/documents/queryoptions/actions.js | 120 --- app/addons/documents/queryoptions/actiontypes.js | 29 - app/addons/documents/queryoptions/queryoptions.js | 446 ----------- app/addons/documents/queryoptions/stores.js | 317 -------- .../tests/queryoptions.componentsSpec.js | 190 ----- .../queryoptions/tests/queryoptions.storesSpec.js | 139 ---- app/addons/documents/resources.js | 395 ---------- app/addons/documents/routes-documents.js | 21 +- app/addons/documents/routes-index-editor.js | 58 +- app/addons/documents/shared-routes.js | 38 +- .../sidebar/SidebarControllerContainer.js | 44 ++ .../header.actiontypes.js => sidebar/reducers.js} | 19 +- app/addons/documents/sidebar/sidebar.js | 11 +- app/addons/documents/tests/document-test-helper.js | 13 +- .../tests/nightwatch/viewCreateBadView.js | 2 + app/addons/permissions/layout.js | 3 +- 57 files changed, 1035 insertions(+), 5882 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 572823b..58a6830 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -182,7 +182,10 @@ module.exports = function (grunt) { exec: { start_nightWatch: { command: __dirname + '/node_modules/nightwatch/bin/nightwatch' + - ' -c ' + __dirname + '/test/nightwatch_tests/nightwatch.json' + ' -c ' + __dirname + '/test/nightwatch_tests/nightwatch.json', + options: { + maxBuffer: 1000 * 1024 + } } }, diff --git a/app/addons/components/components/toggleheaderbutton.js b/app/addons/components/components/toggleheaderbutton.js index 782c369..baaf9c4 100644 --- a/app/addons/components/components/toggleheaderbutton.js +++ b/app/addons/components/components/toggleheaderbutton.js @@ -29,8 +29,8 @@ export const ToggleHeaderButton = React.createClass({ }, render () { - const { iconDefaultClass, fonticon, innerClasses, selected, containerClasses, title, disabled, text, toggleCallback } = this.props; - const selectedBtnClass = (selected) ? 'js-headerbar-togglebutton-selected' : ''; + const { iconDefaultClass, fonticon, innerClasses, selected, containerClasses, title, disabled, text, toggleCallback, active } = this.props; + const selectedBtnClass = (selected || active) ? 'js-headerbar-togglebutton-selected' : ''; return ( ; json = - Cancel - - - ); - } -}); - -var QueryOptionsController = React.createClass({ - getStoreState () { - return { - isVisible: store.isVisible() - }; - }, - - getInitialState () { - return this.getStoreState(); - }, - - onChange () { - this.setState(this.getStoreState()); - }, - - componentDidMount: function () { - store.on('change', this.onChange, this); - }, - - componentWillUnmount: function () { - store.off('change', this.onChange); - }, - - getWrap: function () { - if (!this.TrayWrapper) { - this.TrayWrapper = connectToStores(TrayWrapper, [store], function () { - - return { - includeDocs: store.includeDocs(), - showBetweenKeys: store.showBetweenKeys(), - showByKeys: store.showByKeys(), - betweenKeys: store.betweenKeys(), - byKeys: store.byKeys(), - descending: store.descending(), - skip: store.skip(), - limit: store.limit(), - showReduce: store.showReduce(), - reduce: store.reduce(), - groupLevel: store.groupLevel(), - contentVisible: store.getTrayVisible(), - queryParams: store.getQueryParams() - }; - }); - } - - return this.TrayWrapper; - }, - - render: function () { - if (!this.state.isVisible) { return null;} - const TrayWrapper = this.getWrap(); - return ( -
-
- - - -
-
- ); - } -}); - -var QueryTray = React.createClass({ - - propTypes: { - contentVisible: React.PropTypes.bool.isRequired - }, - - runQuery: function (e) { - e.preventDefault(); - - // we're going to have a fresh collection, purge the cached offset! - PaginationActions.deleteCachedOffset(); - Actions.runQuery(this.props.queryParams); - this.toggleTrayVisibility(); - }, - - toggleTrayVisibility: function () { - Actions.toggleQueryBarVisibility(!this.props.contentVisible); - }, - - closeTray: function () { - Actions.toggleQueryBarVisibility(false); - }, - - toggleIncludeDocs: function () { - Actions.toggleIncludeDocs(); - }, - - toggleReduce: function () { - if (this.props.includeDocs) { - this.toggleIncludeDocs(); - } - Actions.toggleReduce(); - }, - - getTray: function () { - return ( - - -
- - - - - -
- ); - - }, - - render: function () { - return ( -
- - {this.getTray()} -
- ); - } - -}); - -export default { - QueryOptionsController, - QueryButtons, - MainFieldsView, - KeySearchFields, - AdditionalParams, - render: function (el) { - ReactDOM.render(, $(el)[0]); - } -}; diff --git a/app/addons/documents/queryoptions/stores.js b/app/addons/documents/queryoptions/stores.js deleted file mode 100644 index e56d058..0000000 --- a/app/addons/documents/queryoptions/stores.js +++ /dev/null @@ -1,317 +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 FauxtonAPI from '../../../core/api'; -import ActionTypes from './actiontypes'; -const Stores = {}; - -Stores.QueryOptionsStore = FauxtonAPI.Store.extend({ - - initialize () { - this._trayVisible = false; - this.reset(); - }, - - reset () { - this._isVisible = true; - this._loading = true; - this._showByKeys = false; - this._showBetweenKeys = false; - this._includeDocs = false; - this._betweenKeys = { - include: true - }; - this._byKeys = ''; - this._descending = false; - this._skip = ''; - this._limit = "none"; - this._reduce = false; - this._groupLevel = 'exact'; - - this._showReduce = false; - }, - - isLoading () { - return this._loading; - }, - - isVisible () { - return this._isVisible; - }, - - hideQueryOptions () { - this._isVisible = false; - }, - - showQueryOptions () { - this._isVisible = true; - }, - - setTrayVisible (trayVisible) { - this._trayVisible = trayVisible; - }, - - getTrayVisible () { - return this._trayVisible; - }, - - showReduce () { - return this._showReduce; - }, - - reduce () { - return this._reduce; - }, - - betweenKeys () { - return this._betweenKeys; - }, - - updateBetweenKeys (newBetweenKeys) { - this._betweenKeys = newBetweenKeys; - }, - - updateSkip (skip) { - this._skip = skip; - }, - - skip () { - return this._skip; - }, - - limit () { - return this._limit; - }, - - updateLimit (limit) { - this._limit = limit; - }, - - byKeys () { - return this._byKeys; - }, - - updateByKeys (keys) { - this._byKeys = keys; - }, - - includeDocs () { - return this._includeDocs; - }, - - descending () { - return this._descending; - }, - - groupLevel () { - return this._groupLevel; - }, - - toggleByKeys () { - this._showByKeys = !this._showByKeys; - - if (this._showByKeys) { - this._showBetweenKeys = false; - } - }, - - toggleBetweenKeys () { - this._showBetweenKeys = !this._showBetweenKeys; - - if (this._showBetweenKeys) { - this._showByKeys = false; - } - }, - - showByKeys () { - return this._showByKeys; - }, - - showBetweenKeys () { - return this._showBetweenKeys; - }, - - updateGroupLevel (groupLevel) { - this._groupLevel = groupLevel; - }, - - sanitize (params) { - if (params.startkey) { - params.start_key = params.startkey; - delete params.startkey; - } - - if (params.endkey) { - params.end_key = params.endkey; - delete params.endkey; - } - - }, - - setQueryParams (params) { - this.reset(); - this.sanitize(params); - if (params.include_docs) { - this._includeDocs = true; - } - - if (params.start_key || params.end_key) { - let include = true; - - if (params.inclusive_end) { - include = params.inclusive_end === 'true'; - } - this._betweenKeys = { include: include }; - if (params.start_key) { - this._betweenKeys.startkey = params.start_key; - } - if (params.end_key) { - this._betweenKeys.endkey = params.end_key; - } - this._showBetweenKeys = true; - - } else if (params.keys) { - this._byKeys = params.keys; - this._showByKeys = true; - } - - if (params.limit && params.limit !== 'none') { - this._limit = params.limit; - } - - if (params.skip) { - this._skip = params.skip; - } - - if (params.descending) { - this._descending = params.descending; - } - - if (params.reduce) { - if (params.group) { - this._groupLevel = 'exact'; - } else { - this._groupLevel = params.group_level; - } - this._reduce = true; - } - }, - - getQueryParams () { - const params = {}; - - if (this._includeDocs) { - params.include_docs = this._includeDocs; - } - - if (this._showBetweenKeys) { - const betweenKeys = this._betweenKeys; - params.inclusive_end = betweenKeys.include; - if (betweenKeys.startkey && betweenKeys.startkey !== '') { - params.start_key = betweenKeys.startkey; - } - if (betweenKeys.endkey && betweenKeys.endkey !== '') { - params.end_key = betweenKeys.endkey; - } - } else if (this._showByKeys) { - params.keys = this._byKeys.replace(/\r?\n/g, ''); - } - - if (this._limit !== 'none') { - params.limit = parseInt(this._limit, 10); - } - - if (this._skip) { - params.skip = parseInt(this._skip, 10); - } - - if (this._descending) { - params.descending = this._descending; - } - - if (this._reduce) { - params.reduce = true; - - if (this._groupLevel === 'exact') { - params.group = true; - } else { - params.group_level = this._groupLevel; - } - } - - return params; - }, - - getIncludeDocsEnabled () { - return this._includeDocs; - }, - - dispatch (action) { - switch (action.type) { - case ActionTypes.QUERY_RESET: - this.setQueryParams(action.params); - break; - case ActionTypes.QUERY_TOGGLE_INCLUDE_DOCS: - this._includeDocs = !this._includeDocs; - break; - case ActionTypes.QUERY_TOGGLE_DESCENDING: - this._descending = !this._descending; - break; - case ActionTypes.QUERY_TOGGLE_BY_KEYS: - this.toggleByKeys(); - break; - case ActionTypes.QUERY_TOGGLE_BETWEEN_KEYS: - this.toggleBetweenKeys(); - break; - case ActionTypes.QUERY_UPDATE_BETWEEN_KEYS: - this.updateBetweenKeys(action.betweenKeys); - break; - case ActionTypes.QUERY_UPDATE_BY_KEYS: - this.updateByKeys(action.byKeys); - break; - case ActionTypes.QUERY_UPDATE_SKIP: - this.updateSkip(action.skip); - break; - case ActionTypes.QUERY_UPDATE_LIMIT: - this.updateLimit(action.limit); - break; - case ActionTypes.QUERY_SHOW_REDUCE: - this._showReduce = true; - break; - case ActionTypes.QUERY_TOGGLE_REDUCE: - this._reduce = !this._reduce; - break; - case ActionTypes.QUERY_UPDATE_GROUP_LEVEL: - this.updateGroupLevel(action.groupLevel); - break; - case ActionTypes.QUERY_UPDATE_VISIBILITY: - this.setTrayVisible(action.options); - break; - case ActionTypes.QUERY_HIDE: - this.hideQueryOptions(); - break; - case ActionTypes.QUERY_SHOW: - this.showQueryOptions(); - break; - default: - return; - // do nothing - } - this.triggerChange(); - - } -}); - -Stores.queryOptionsStore = new Stores.QueryOptionsStore(); -Stores.queryOptionsStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.queryOptionsStore.dispatch); - -export default Stores; diff --git a/app/addons/documents/queryoptions/tests/queryoptions.componentsSpec.js b/app/addons/documents/queryoptions/tests/queryoptions.componentsSpec.js deleted file mode 100644 index afcc851..0000000 --- a/app/addons/documents/queryoptions/tests/queryoptions.componentsSpec.js +++ /dev/null @@ -1,190 +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 FauxtonAPI from "../../../../core/api"; -import Views from "../queryoptions"; -import utils from "../../../../../test/mocha/testUtils"; -import React from "react"; -import ReactDOM from "react-dom"; -import TestUtils from "react-addons-test-utils"; -import sinon from "sinon"; -FauxtonAPI.router = new FauxtonAPI.Router([]); - -var assert = utils.assert; -var restore = utils.restore; - -describe('Query Options', function () { - var container; - - beforeEach(function () { - container = document.createElement('div'); - }); - - afterEach(function () { - ReactDOM.unmountComponentAtNode(container); - }); - - describe('MainFieldsView', function () { - var mainFieldsEl; - - it('returns null no reduce', function () { - mainFieldsEl = TestUtils.renderIntoDocument(, container); - assert.ok(_.isNull(mainFieldsEl.reduce())); - }); - - it('shows reduce and group level', function () { - mainFieldsEl = TestUtils.renderIntoDocument(, container); - assert.ok(!_.isNull(mainFieldsEl.reduce())); - assert.ok(!_.isNull(mainFieldsEl.groupLevel())); - }); - - it('updates group level', function () { - var spy = sinon.spy(); - mainFieldsEl = TestUtils.renderIntoDocument(, container); - var el = $(ReactDOM.findDOMNode(mainFieldsEl)).find('#qoGroupLevel')[0]; - TestUtils.Simulate.change(el, {target: {value: 'exact'}}); - - assert.ok(spy.calledOnce); - }); - - it('toggles include docs on change', function () { - var spy = sinon.spy(); - mainFieldsEl = TestUtils.renderIntoDocument(, container); - var el = $(ReactDOM.findDOMNode(mainFieldsEl)).find('#qoIncludeDocs')[0]; - TestUtils.Simulate.change(el); - - assert.ok(spy.calledOnce); - }); - - it('uses overridden URL prop if defined', function () { - var customDocURL = 'http://whatever.com'; - mainFieldsEl = TestUtils.renderIntoDocument( - , - container); - assert.equal($(ReactDOM.findDOMNode(mainFieldsEl)).find('.help-link').attr('href'), customDocURL); - }); - - }); - - describe('KeySearchFields', function () { - - it('toggles keys', function () { - var spy = sinon.spy(); - var keysEl = TestUtils.renderIntoDocument(, container); - - var el = $(ReactDOM.findDOMNode(keysEl)).find('#byKeys')[0]; - TestUtils.Simulate.click(el); - assert.ok(spy.calledOnce); - }); - - it('toggles between keys', function () { - var spy = sinon.spy(); - var keysEl = TestUtils.renderIntoDocument(, container); - - var el = $(ReactDOM.findDOMNode(keysEl)).find('#betweenKeys')[0]; - TestUtils.Simulate.click(el); - assert.ok(spy.calledOnce); - }); - - it('updates byKeys', function () { - var spy = sinon.spy(); - var keysEl = TestUtils.renderIntoDocument(, container); - - var el = $(ReactDOM.findDOMNode(keysEl)).find('#keys-input')[0]; - TestUtils.Simulate.change(el, {target: {value: 'boom'}}); - assert.ok(spy.calledWith('boom')); - }); - - it('updates betweenKeys', function () { - var spy = sinon.spy(); - var betweenKeys = { - startkey: '', - endkey: '', - inclusive_end: true - }; - var keysEl = TestUtils.renderIntoDocument(, container); - - var el = $(ReactDOM.findDOMNode(keysEl)).find('#endkey')[0]; - TestUtils.Simulate.change(el, {target: {value: 'boom'}}); - assert.ok(spy.calledOnce); - }); - }); - - describe('AdditionalParams', function () { - afterEach(function () { - restore(FauxtonAPI.addNotification); - }); - - it('only updates skip with numbers', function () { - var paramsEl = TestUtils.renderIntoDocument(, container); - - var spy = sinon.spy(FauxtonAPI, 'addNotification'); - paramsEl.updateSkip({target: {value: 'b'}, preventDefault: function () {}}); - - assert.ok(spy.calledOnce); - }); - - it('updates skip if a number', function () { - var val = 0; - var paramsEl = TestUtils.renderIntoDocument(, container); - - paramsEl.updateSkip({target: {value: '3'}, preventDefault: function () {}}); - assert.equal(val, '3'); - }); - }); - -}); - -describe('QueryButtons', function () { - var container; - - beforeEach(function () { - container = document.createElement('div'); - }); - - afterEach(function () { - ReactDOM.unmountComponentAtNode(container); - }); - - describe('cancel event fires', function () { - var spy = sinon.spy(); - var component = TestUtils.renderIntoDocument(, container); - TestUtils.Simulate.click($(ReactDOM.findDOMNode(component)).find('a')[0]); - assert.ok(spy.calledOnce); - }); - -}); diff --git a/app/addons/documents/queryoptions/tests/queryoptions.storesSpec.js b/app/addons/documents/queryoptions/tests/queryoptions.storesSpec.js deleted file mode 100644 index cb9de3c..0000000 --- a/app/addons/documents/queryoptions/tests/queryoptions.storesSpec.js +++ /dev/null @@ -1,139 +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 FauxtonAPI from "../../../../core/api"; -import Stores from "../stores"; -import testUtils from "../../../../../test/mocha/testUtils"; -var assert = testUtils.assert; -var dispatchToken; -var store; - -describe('QueryOptions Store', () => { - beforeEach(() => { - store = new Stores.QueryOptionsStore(); - dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch); - }); - - afterEach(() => { - FauxtonAPI.dispatcher.unregister(dispatchToken); - }); - - describe('Toggle By Keys and Between Keys', () => { - - it('toggling by keys sets by keys to true', () => { - - store.toggleByKeys(); - - assert.ok(store.showByKeys()); - }); - - it('toggling between keys sets between keys to true', () => { - - store.toggleBetweenKeys(); - assert.ok(store.showBetweenKeys()); - }); - - it('toggling between keys sets by keys to false', () => { - store._showByKeys = true; - store.toggleBetweenKeys(); - assert.notOk(store.showByKeys()); - }); - - it('toggling by keys sets between keys to false', () => { - store._showBetweenKeys = true; - store.toggleByKeys(); - assert.notOk(store.showBetweenKeys()); - }); - - }); - - describe('getQueryParams', () => { - it('returns params for default', () => { - assert.deepEqual(store.getQueryParams(), {}); - }); - - it('with betweenKeys', () => { - store.toggleBetweenKeys(); - store.updateBetweenKeys({ - startkey:"a", - endkey: "z", - include: true - }); - - assert.deepEqual(store.getQueryParams(), { - inclusive_end: true, - start_key: 'a', - end_key: 'z' - }); - }); - - it('with byKeys', () => { - store.toggleByKeys(); - store.updateByKeys("[1,2,3]"); - - assert.deepEqual(store.getQueryParams(), { - keys: "[1,2,3]" - }); - }); - }); - - describe('setQueryParams', () => { - - it('sets all store values from given params', () => { - - store.setQueryParams({ - include_docs: true, - limit: 10, - skip: 5, - descending: true - }); - - assert.ok(store.includeDocs()); - assert.ok(store.descending()); - assert.equal(store.limit(), 10); - assert.equal(store.skip(), 5); - }); - - it('sets between keys', () => { - store.setQueryParams({ - start_key: 1, - end_key: 5 - }); - - assert.equal(store.betweenKeys().startkey, 1); - assert.equal(store.betweenKeys().endkey, 5); - assert.ok(store.betweenKeys().include); - assert.ok(store.showBetweenKeys()); - }); - - it('sets by keys', () => { - store.setQueryParams({ - keys: [1, 2, 3] - }); - - assert.deepEqual(store.byKeys(), [1, 2, 3]); - assert.ok(store.showByKeys()); - }); - - it('works with startkey and endkey', () => { - store.setQueryParams({ - startkey: 1, - endkey: 5 - }); - - assert.equal(store.betweenKeys().startkey, 1); - assert.equal(store.betweenKeys().endkey, 5); - assert.ok(store.showBetweenKeys()); - }); - - }); -}); diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js index 8a19d09..15a394a 100644 --- a/app/addons/documents/resources.js +++ b/app/addons/documents/resources.js @@ -13,9 +13,6 @@ import app from "../../app"; import FauxtonAPI from "../../core/api"; import Documents from "./shared-resources"; -import PagingCollection from "../../../assets/js/plugins/cloudant.pagingcollection"; -import Constants from './constants'; - Documents.UUID = FauxtonAPI.Model.extend({ initialize: function (options) { @@ -86,258 +83,6 @@ Documents.DdocInfo = FauxtonAPI.Model.extend({ } }); -Documents.MangoIndex = Documents.Doc.extend({ - idAttribute: 'ddoc', - - getId: function () { - - if (this.id) { - return this.id; - } - - - return '_all_docs'; - }, - - isNew: function () { - // never use put - return true; - }, - - // @deprecated, see isJSONDocBulkDeletable - isDeletable: function () { - return this.get('type') !== 'special'; - }, - - // @deprecated, see isJSONDocBulkDeletable - isBulkDeletable: function () { - return this.isDeletable(); - }, - - isFromView: function () { - return false; - }, - - url: function () { - var database = this.database.safeID(); - - return FauxtonAPI.urls('mango', 'index-server', database); - } -}); - -Documents.MangoIndexCollection = PagingCollection.extend({ - model: Documents.MangoIndex, - initialize: function (_attr, options) { - var defaultLimit = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE; - - this.database = options.database; - this.params = _.extend({limit: defaultLimit}, options.params); - }, - - collectionType: Constants.INDEX_RESULTS_DOC_TYPE.MANGO_INDEX, - - url: function () { - return this.urlRef.apply(this, arguments); - }, - - updateSeq: function () { - return false; - }, - - //@deprecated, see isJSONDocEditable - isEditable: function () { - return false; - }, - - parse: function (res) { - return res.indexes; - }, - - urlRef: function (context) { - var database = this.database.safeID(), - query = ''; - - if (!context) { - context = 'index-server'; - } - - return FauxtonAPI.urls('mango', context, database, query); - } -}); - -// MANGO INDEX EDITOR -Documents.MangoDoc = Documents.Doc.extend({ - isMangoDoc: function () { - return true; - } -}); - -Documents.MangoDocumentCollection = PagingCollection.extend({ - model: Documents.MangoDoc, - - collectionType: 'MangoDocumentCollection', - - initialize: function (_attr, options) { - var defaultLimit = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE; - - this._warning = null; - this.database = options.database; - this.params = _.extend({limit: defaultLimit}, options.params); - - this.paging = _.defaults((options.paging || {}), { - defaultParams: _.defaults({}, options.params), - hasNext: false, - hasPrevious: false, - params: {}, - pageSize: FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE, - direction: undefined - }); - - this.paging.params = _.clone(this.paging.defaultParams); - }, - - url: function () { - return this.urlRef.apply(this, arguments); - }, - - warning: function () { - return this._warning; - }, - - updateSeq: function () { - return false; - }, - - isEditable: function () { - return true; - }, - - setQuery: function (query) { - this.query = query; - return this; - }, - - pageSizeReset: function (pageSize, opts) { - var options = _.defaults((opts || {}), {fetch: true}); - this.paging.direction = undefined; - this.paging.pageSize = pageSize; - this.paging.params = this.paging.defaultParams; - this.paging.params.limit = pageSize; - - if (options.fetch) { - return this.fetch(); - } - }, - - _iterate: function (offset) { - this.paging.params = this.calculateParams(this.paging.params, offset, this.paging.pageSize); - - return this.fetch(); - }, - - getPaginatedQuery: function () { - if (!this.query) { - return this.query; - } - - var paginatedQuery = JSON.parse(JSON.stringify(this.query)); - - if (!this.paging.direction && this.paging.params.limit > 0) { - this.paging.direction = 'fetch'; - this.paging.params.limit = this.paging.params.limit + 1; - } - - // just update if NOT provided by editor - if (!paginatedQuery.limit) { - paginatedQuery.limit = this.paging.params.limit; - } - - if (!paginatedQuery.skip) { - paginatedQuery.skip = this.paging.params.skip; - } - - return paginatedQuery; - }, - - fetch: function () { - var url = this.urlRef(), - promise = FauxtonAPI.Deferred(), - query = this.getPaginatedQuery(); - - //this section can get called when updating page - //and the query options might not have been selected yet - //so we just return and don't do a fetch - if (!query) { - promise.resolve(); - return promise; - } - - $.ajax({ - type: 'POST', - url: url, - contentType: 'application/json', - dataType: 'json', - data: JSON.stringify(query), - }) - .then(res => { - this.handleResponse(res, promise); - }, res => { - promise.reject(res.responseJSON); - }); - - return promise; - }, - - parse: function (resp) { - var rows = []; - - rows = resp.docs; - - this._warning = resp.warning; - - this.viewMeta = { - total_rows: resp.total_rows, - offset: resp.offset, - update_seq: resp.update_seq - }; - - this.paging.hasNext = this.paging.hasPrevious = false; - - if (this.paging.params.skip > 0) { - this.paging.hasPrevious = true; - } - - if (rows.length === this.paging.pageSize + 1) { - this.paging.hasNext = true; - - // remove the next page marker result - rows.pop(); - this.viewMeta.total_rows = this.viewMeta.total_rows - 1; - } - - return rows; - }, - - handleResponse: function (res, promise) { - var models = this.parse(res); - - this.reset(models); - - promise.resolve(); - }, - - urlRef: function (context) { - var database = this.database.safeID(), - query = ''; - - if (!context) { - context = 'query-server'; - } - - return FauxtonAPI.urls('mango', context, database, query); - } -}); - Documents.NewDoc = Documents.Doc.extend({ fetch: function () { var uuid = new Documents.UUID(); @@ -469,144 +214,4 @@ Documents.MangoBulkDeleteDocCollection = Documents.BulkDeleteDocCollection.exten }); -Documents.IndexCollection = PagingCollection.extend({ - model: Documents.Doc, - documentation: function () { - return FauxtonAPI.constants.DOC_URLS.GENERAL; - }, - initialize: function (_models, options) { - this.database = options.database; - this.params = _.extend({limit: 20, reduce: false}, options.params); - - this.idxType = "_view"; - this.view = options.view; - this.design = options.design.replace('_design/', ''); - this.perPageLimit = options.perPageLimit || 20; - - if (!this.params.limit) { - this.params.limit = this.perPageLimit; - } - }, - - isEditable: function () { - return !this.params.reduce; - }, - - urlRef: function (context, params) { - var query = ""; - - if (params) { - if (!_.isEmpty(params)) { - query = $.param(params); - } else { - query = ''; - } - } else if (this.params) { - var parsedParam = Documents.QueryParams.stringify(this.params); - query = $.param(parsedParam); - } - - if (!context) { - context = 'server'; - } - - var database = this.database.safeID(), - design = app.utils.safeURLName(this.design), - view = app.utils.safeURLName(this.view), - url = FauxtonAPI.urls('view', context, database, design, view); - - return (url.indexOf("?") > -1) ? `${url}&${query}` : `${url}?${query}`; - }, - - url: function () { - return this.urlRef.apply(this, arguments); - }, - - totalRows: function () { - if (this.params.reduce) { return "unknown_reduce";} - - return this.viewMeta.total_rows || "unknown"; - }, - - updateSeq: function () { - if (!this.viewMeta) { - return false; - } - - return this.viewMeta.update_seq || false; - }, - - simple: function () { - var docs = this.map(function (item) { - return { - _id: item.id, - key: item.get('key'), - value: item.get('value') - }; - }); - - return new Documents.IndexCollection(docs, { - database: this.database, - params: this.params, - view: this.view, - design: this.design - }); - }, - - parse: function () { - this.endTime = new Date().getTime(); - this.requestDuration = (this.endTime - this.startTime); - - return PagingCollection.prototype.parse.apply(this, arguments); - }, - - buildAllDocs: function () { - this.fetch(); - }, - - // We implement our own fetch to store the starttime so we that - // we can get the request duration - fetch: function () { - this.startTime = new Date().getTime(); - return PagingCollection.prototype.fetch.call(this); - }, - - allDocs: function () { - return this.models; - }, - - // This is taken from futon.browse.js $.timeString - requestDurationInString: function () { - var ms, sec, min, h, timeString, milliseconds = this.requestDuration; - - sec = Math.floor(milliseconds / 1000.0); - min = Math.floor(sec / 60.0); - sec = (sec % 60.0).toString(); - if (sec.length < 2) { - sec = "0" + sec; - } - - h = (Math.floor(min / 60.0)).toString(); - if (h.length < 2) { - h = "0" + h; - } - - min = (min % 60.0).toString(); - if (min.length < 2) { - min = "0" + min; - } - - timeString = h + ":" + min + ":" + sec; - - ms = (milliseconds % 1000.0).toString(); - while (ms.length < 3) { - ms = "0" + ms; - } - timeString += "." + ms; - - return timeString; - } - -}); - export default Documents; diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js index 463c2d3..ceb33a3 100644 --- a/app/addons/documents/routes-documents.js +++ b/app/addons/documents/routes-documents.js @@ -10,20 +10,15 @@ // License for the specific language governing permissions and limitations under // the License. -//import app from '../../app'; import React from 'react'; import FauxtonAPI from '../../core/api'; import BaseRoute from './shared-routes'; -//import Documents from './resources'; import ChangesActions from './changes/actions'; import Databases from '../databases/base'; import Resources from './resources'; -//import IndexResultStores from './index-results/stores'; -//import IndexResultsActions from './index-results/actions'; import SidebarActions from './sidebar/actions'; import DesignDocInfoActions from './designdocinfo/actions'; import ComponentsActions from '../components/actions'; -import QueryOptionsActions from './queryoptions/actions'; import {DocsTabsSidebarLayout, ViewsTabsSidebarLayout, ChangesSidebarLayout} from './layouts'; var DocumentsRouteObject = BaseRoute.extend({ @@ -64,8 +59,6 @@ var DocumentsRouteObject = BaseRoute.extend({ designDocSection: 'metadata' }); - QueryOptionsActions.hideQueryOptions(); - const dropDownLinks = this.getCrumbs(this.database); return ; }, @@ -124,8 +119,6 @@ var DocumentsRouteObject = BaseRoute.extend({ }); SidebarActions.selectNavItem('changes'); - QueryOptionsActions.hideQueryOptions(); - return ; }, diff --git a/app/addons/documents/shared-routes.js b/app/addons/documents/shared-routes.js index 15f5b44..ef0d2bd 100644 --- a/app/addons/documents/shared-routes.js +++ b/app/addons/documents/shared-routes.js @@ -13,10 +13,7 @@ import app from "../../app"; import FauxtonAPI from "../../core/api"; import Documents from "./shared-resources"; -import PaginationActions from "./pagination/actions"; -import IndexResultStores from "./index-results/stores"; import SidebarActions from "./sidebar/actions"; -import QueryActions from './queryoptions/actions'; // The Documents section is built up a lot of different route object which share code. This contains // base functionality that can be used across routes / addons @@ -38,25 +35,6 @@ var BaseRoute = FauxtonAPI.RouteObject.extend({ }); }, - showQueryOptions: function (urlParams, ddoc, viewName) { - var promise = this.designDocs.fetch({reset: true}), - hasReduceFunction; - - promise.then(() => { - var design = _.findWhere(this.designDocs.models, {id: '_design/' + ddoc}); - !_.isUndefined(hasReduceFunction = design.attributes.doc.views[viewName].reduce); - - QueryActions.showQueryOptions(); - QueryActions.reset({ - queryParams: urlParams, - hasReduce: hasReduceFunction, - showReduce: !_.isUndefined(hasReduceFunction), - viewName: viewName, - ddocName: ddoc - }); - }); - }, - addSidebar: function (selectedNavItem) { var options = { designDocs: this.designDocs, @@ -78,23 +56,11 @@ var BaseRoute = FauxtonAPI.RouteObject.extend({ createParams: function (options) { const urlParams = app.getParams(options), - params = Documents.QueryParams.parse(urlParams), - store = IndexResultStores.indexResultsStore; - - let start = 0; - if (urlParams.skip && store.hasCachedOffset()) { - start = Math.max(store.getCachedOffset(), parseInt(urlParams.skip, 10)); - } else if (urlParams.skip) { - start = parseInt(urlParams.skip, 10); - } else if (store.hasCachedOffset()) { - start = store.getCachedOffset(); - } - PaginationActions.setPageStart(start); - PaginationActions.setDocumentLimit(parseInt(urlParams.limit, 10)); + params = Documents.QueryParams.parse(urlParams); return { urlParams: urlParams, - docParams: _.extend(params, {limit: store.getPerPage()}) + docParams: params }; } }); diff --git a/app/addons/documents/sidebar/SidebarControllerContainer.js b/app/addons/documents/sidebar/SidebarControllerContainer.js new file mode 100644 index 0000000..4da0628 --- /dev/null +++ b/app/addons/documents/sidebar/SidebarControllerContainer.js @@ -0,0 +1,44 @@ +// 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 { connect } from 'react-redux'; +import SidebarComponents from './sidebar'; +import ActionTypes from './actiontypes'; + +const reduxUpdatedDesignDocList = (designDocs) => { + return { + type: ActionTypes.SIDEBAR_UPDATED_DESIGN_DOCS, + options: { + designDocs: Array.isArray(designDocs) ? designDocs : [] + } + }; +}; + +const mapStateToProps = () => { + return {}; +}; + +const mapDispatchToProps = (dispatch) => { + return { + reduxUpdatedDesignDocList: (designDocsList) => { + dispatch(reduxUpdatedDesignDocList(designDocsList)); + } + }; +}; + +const SidebarControllerContainer = connect( + mapStateToProps, + mapDispatchToProps +)(SidebarComponents.SidebarController); + +export default SidebarControllerContainer; + diff --git a/app/addons/documents/header/header.actiontypes.js b/app/addons/documents/sidebar/reducers.js similarity index 60% rename from app/addons/documents/header/header.actiontypes.js rename to app/addons/documents/sidebar/reducers.js index 7d5d9b7..ef295f4 100644 --- a/app/addons/documents/header/header.actiontypes.js +++ b/app/addons/documents/sidebar/reducers.js @@ -10,6 +10,21 @@ // License for the specific language governing permissions and limitations under // the License. -export default { - TOGGLE_LAYOUT: 'TOGGLE_LAYOUT', +import ActionTypes from './actiontypes'; + +const initialState = { + designDocs: [] +}; + +export default function resultsState(state = initialState, action) { + switch (action.type) { + + case ActionTypes.SIDEBAR_UPDATED_DESIGN_DOCS: + return Object.assign({}, state, { + designDocs: action.options.designDocs + }); + + default: + return state; + } }; diff --git a/app/addons/documents/sidebar/sidebar.js b/app/addons/documents/sidebar/sidebar.js index 79daa6e..f5cd83a 100644 --- a/app/addons/documents/sidebar/sidebar.js +++ b/app/addons/documents/sidebar/sidebar.js @@ -481,7 +481,16 @@ var SidebarController = React.createClass({ }, onChange: function () { - this.setState(this.getStoreState()); + + const newState = this.getStoreState(); + // Workaround to signal Redux store that the design doc list was updated + // which is currently required by QueryOptionsContainer + // It should be removed once Sidebar components are refactored to use Redux + if (this.props.reduxUpdatedDesignDocList) { + this.props.reduxUpdatedDesignDocList(newState.designDocList); + } + + this.setState(newState); }, showDeleteDatabaseModal: function (payload) { diff --git a/app/addons/documents/tests/document-test-helper.js b/app/addons/documents/tests/document-test-helper.js index bb03781..b343a81 100644 --- a/app/addons/documents/tests/document-test-helper.js +++ b/app/addons/documents/tests/document-test-helper.js @@ -11,7 +11,7 @@ // the License. import Documents from "../resources"; -var opts = { +const opts = { params: {}, database: { safeID: function () { return '1';} @@ -26,15 +26,6 @@ function createDocColumn (docs) { return new Documents.AllDocs(docs, opts); } -function createMangoIndexDocColumn (docs) { - docs = docs.map(function (doc) { - return Documents.MangoIndex.prototype.parse(doc); - }); - - return new Documents.MangoIndexCollection(docs, opts); -} - export default { - createDocColumn: createDocColumn, - createMangoIndexDocColumn: createMangoIndexDocColumn + createDocColumn }; diff --git a/app/addons/documents/tests/nightwatch/viewCreateBadView.js b/app/addons/documents/tests/nightwatch/viewCreateBadView.js index fc46ebc..4b3b2fa 100644 --- a/app/addons/documents/tests/nightwatch/viewCreateBadView.js +++ b/app/addons/documents/tests/nightwatch/viewCreateBadView.js @@ -42,6 +42,7 @@ module.exports = { .clickWhenVisible('.control-toggle-queryoptions', waitTime, false) .clickWhenVisible('label[for="qoReduce"]', waitTime, false) .clickWhenVisible('.query-options .btn-secondary', waitTime, false) + .waitForElementVisible('div.table-view-docs', waitTime, false) .waitForAttribute('.table-view-docs td:nth-child(4)', 'title', function (docContents) { return (/_sum function requires/).test(docContents); }) @@ -60,6 +61,7 @@ module.exports = { .clickWhenVisible('.control-toggle-queryoptions', waitTime, false) .clickWhenVisible('label[for="qoReduce"]', waitTime, false) .clickWhenVisible('.query-options .btn-secondary', waitTime, false) + .waitForElementVisible('div.table-view-docs', waitTime, false) .waitForAttribute('.table-view-docs td:nth-child(4)', 'title', function (docContents) { return (/_sum function requires/).test(docContents); }) diff --git a/app/addons/permissions/layout.js b/app/addons/permissions/layout.js index dc8558d..2569911 100644 --- a/app/addons/permissions/layout.js +++ b/app/addons/permissions/layout.js @@ -19,7 +19,8 @@ export const PermissionsLayout = ({docURL, database, endpoint, dbName, dropDownL return (
'].