You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ro...@apache.org on 2015/03/31 17:20:12 UTC
fauxton commit: updated refs/heads/master to d7641f1
Repository: couchdb-fauxton
Updated Branches:
refs/heads/master 4a40e3f9e -> d7641f16e
Mango: creation and listing of indexes
Part 1/2 for Mango:
Creation of Mango indexes and listing them. Disabled pagination
and bulk-deletion for now, see:
https://issues.apache.org/jira/browse/COUCHDB-2651
https://issues.apache.org/jira/browse/COUCHDB-2652
Use the direct urls to access the features:
http://localhost:8000/#database/$YOUR_DATABASE/_index
http://localhost:8000/#database/$YOUR_DATABASE/_indexlist
Additionally prepares the app for i18n.
Additionally removes the listing of Mango created indexes which
are not editable from the sidebar
COUCHDB-2627
PR: #343
PR-URL: https://github.com/apache/couchdb-fauxton/pull/343
Reviewed-By: garren smith <ga...@gmail.com>
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/d7641f16
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/d7641f16
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/d7641f16
Branch: refs/heads/master
Commit: d7641f16e7a12faff896136826b43f7affb0a393
Parents: 4a40e3f
Author: Robert Kowalski <ro...@apache.org>
Authored: Fri Mar 27 14:16:43 2015 +0100
Committer: Robert Kowalski <ro...@apache.org>
Committed: Tue Mar 31 17:20:15 2015 +0200
----------------------------------------------------------------------
Gruntfile.js | 7 +
app/addons/components/assets/less/docs.less | 6 +
.../components/react-components.react.jsx | 43 +++--
app/addons/components/tests/docSpec.react.jsx | 19 ++-
app/addons/documents/base.js | 28 ++++
app/addons/documents/index-editor/stores.js | 4 +-
app/addons/documents/index-results/actions.js | 2 +-
.../index-results.components.react.jsx | 16 +-
app/addons/documents/index-results/stores.js | 29 +++-
.../tests/index-results.actionsSpec.js | 8 +
.../tests/index-results.storesSpec.js | 20 +--
app/addons/documents/mango/mango.actions.js | 58 +++++++
app/addons/documents/mango/mango.actiontypes.js | 17 ++
.../documents/mango/mango.components.react.jsx | 156 +++++++++++++++++++
app/addons/documents/mango/mango.stores.js | 63 ++++++++
.../mango/tests/mango.componentsSpec.react.jsx | 93 +++++++++++
app/addons/documents/resources.js | 71 +++++++++
app/addons/documents/routes-documents.js | 29 +---
app/addons/documents/routes-index-editor.js | 4 +-
app/addons/documents/routes-mango.js | 154 ++++++++++++++++++
app/addons/documents/routes.js | 9 +-
app/addons/documents/shared-resources.js | 8 +
app/addons/documents/shared-routes.js | 22 +++
app/addons/documents/shared-views.js | 5 +
app/addons/documents/tests/headerSpec.react.jsx | 2 +-
.../documents/tests/nightwatch/mangoIndex.js | 48 ++++++
.../tests/nightwatch/mangoIndexList.js | 29 ++++
app/addons/documents/tests/resourcesSpec.js | 95 ++++++++++-
.../tests/viewIndex.componentsSpec.react.jsx | 87 ++++++++++-
app/addons/documents/views-mango.js | 66 ++++++++
app/initialize.js.underscore | 3 +-
i18n.json.default | 8 +
package.json | 1 +
tasks/fauxton.js | 11 +-
tasks/helper.js | 11 ++
.../custom-commands/populateDatabase.js | 30 +++-
36 files changed, 1164 insertions(+), 98 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/Gruntfile.js
----------------------------------------------------------------------
diff --git a/Gruntfile.js b/Gruntfile.js
index 781d97d..719f23c 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -121,6 +121,13 @@ module.exports = function (grunt) {
};
var settings = helper.readSettingsFile();
+
+ var i18n = JSON.stringify(helper.readI18nFile(), null, ' ');
+
+ ['development', 'release', 'couchapp'].forEach(function (key) {
+ settings.template[key].app.i18n = i18n;
+ });
+
return settings.template || defaultSettings;
}();
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/components/assets/less/docs.less
----------------------------------------------------------------------
diff --git a/app/addons/components/assets/less/docs.less b/app/addons/components/assets/less/docs.less
index 97957c2..3f8a8f8 100644
--- a/app/addons/components/assets/less/docs.less
+++ b/app/addons/components/assets/less/docs.less
@@ -76,6 +76,12 @@
padding-left: 23px; // 7px to the right-border + 16px around
}
}
+ .checkbox-dummy {
+ width: 20px;
+ height: 20px;
+ padding-left: 23px;
+ margin-right: 15px;
+ }
.doc-item {
width: auto;
overflow: hidden;
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/components/react-components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/react-components.react.jsx b/app/addons/components/react-components.react.jsx
index 91d04cd..26d27cd 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -65,30 +65,35 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
var CodeEditor = React.createClass({
render: function () {
var code = this.aceEditor ? this.aceEditor.getValue() : this.props.code;
- var docsLink;
- if (this.props.docs) {
- docsLink = <a
- className="help-link"
- data-bypass="true"
- href={this.props.docs}
- target="_blank"
- >
- <i className="icon-question-sign"></i>
- </a>;
-
- }
return (
<div className="control-group">
- <label htmlFor="ace-function">
- <strong>{this.props.title}</strong>
- {docsLink}
- </label>
+ {this.getTitleFragment()}
<div className="js-editor" id={this.props.id}>{this.props.code}</div>
<Beautify code={code} beautifiedCode={this.setEditorValue} />
</div>
);
},
+ getTitleFragment: function () {
+ if (!this.props.docs) {
+ return <strong>{this.props.title}</strong>;
+ }
+
+ return (
+ <label>
+ <strong>{this.props.title}</strong>
+ <a
+ className="help-link"
+ data-bypass="true"
+ href={this.props.docs}
+ target="_blank"
+ >
+ <i className="icon-question-sign"></i>
+ </a>;
+ </label>
+ );
+ },
+
setEditorValue: function (code) {
this.aceEditor.setValue(code);
//this is not a good practice normally but because we working with a backbone view as the mapeditor
@@ -213,6 +218,11 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
},
getCheckbox: function () {
+
+ if (!this.props.isDeletable) {
+ return <div className="checkbox-dummy"></div>;
+ }
+
return (
<div className="checkbox inline">
<input
@@ -286,7 +296,6 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
}
});
-
var ReactComponents = {
ConfirmButton: ConfirmButton,
ToggleHeaderButton: ToggleHeaderButton,
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/components/tests/docSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/docSpec.react.jsx b/app/addons/components/tests/docSpec.react.jsx
index 119abe6..be1f7ce 100644
--- a/app/addons/components/tests/docSpec.react.jsx
+++ b/app/addons/components/tests/docSpec.react.jsx
@@ -51,7 +51,7 @@ define([
it('you can check it', function () {
el = TestUtils.renderIntoDocument(
- <ReactComponents.Document checked={true} docIdentifier="foo" />,
+ <ReactComponents.Document isDeletable={true} checked={true} docIdentifier="foo" />,
container
);
assert.equal($(el.getDOMNode()).find('input[type="checkbox"]').attr('checked'), 'checked');
@@ -59,7 +59,7 @@ define([
it('you can uncheck it', function () {
el = TestUtils.renderIntoDocument(
- <ReactComponents.Document docIdentifier="foo" />,
+ <ReactComponents.Document isDeletable={true} docIdentifier="foo" />,
container
);
assert.equal($(el.getDOMNode()).find('input[type="checkbox"]').attr('checked'), undefined);
@@ -69,7 +69,7 @@ define([
var spy = sinon.spy();
el = TestUtils.renderIntoDocument(
- <ReactComponents.Document docChecked={spy} docIdentifier="foo" />,
+ <ReactComponents.Document isDeletable={true} docChecked={spy} docIdentifier="foo" />,
container
);
var testEl = $(el.getDOMNode()).find('input[type="checkbox"]')[0];
@@ -81,12 +81,23 @@ define([
var spy = sinon.spy();
el = TestUtils.renderIntoDocument(
- <ReactComponents.Document onDoubleClick={spy} docIdentifier="foo" />,
+ <ReactComponents.Document isDeletable={true} onDoubleClick={spy} docIdentifier="foo" />,
container
);
React.addons.TestUtils.Simulate.doubleClick(el.getDOMNode());
assert.ok(spy.calledOnce);
});
+
+ it('can render without checkbox', function () {
+ var spy = sinon.spy();
+
+ el = TestUtils.renderIntoDocument(
+ <ReactComponents.Document isDeletable={false} onDoubleClick={spy} docIdentifier="foo" />,
+ container
+ );
+ assert.notOk($(el.getDOMNode()).find('input[type="checkbox"]').length);
+ assert.ok($(el.getDOMNode()).find('.checkbox-dummy').length);
+ });
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/base.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index 776f514..d6ac60d 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -110,5 +110,33 @@ function (app, FauxtonAPI, Documents) {
return '/database/' + database + '/' ;
},
});
+
+ FauxtonAPI.registerUrls('mango', {
+
+ 'index-server': function (db, query) {
+ if (!query) {
+ query = '';
+ }
+
+ return app.host + '/' + db + '/_index' + query;
+ },
+
+ 'index-apiurl': function (db, query) {
+ if (!query) {
+ query = '';
+ }
+
+ return window.location.origin + '/' + db + '/_index' + query;
+ },
+
+ 'index-app': function (db, query) {
+ if (!query) {
+ query = '';
+ }
+
+ return 'database/' + db + '/_index' + query;
+ }
+ });
+
return Documents;
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/index-editor/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/stores.js b/app/addons/documents/index-editor/stores.js
index 2070df3..f9a3606 100644
--- a/app/addons/documents/index-editor/stores.js
+++ b/app/addons/documents/index-editor/stores.js
@@ -71,7 +71,9 @@ function (FauxtonAPI, ActionTypes) {
},
getDesignDocs: function () {
- return this._designDocs;
+ return this._designDocs.filter(function (ddoc) {
+ return ddoc.get('doc').language !== 'query';
+ });
},
getDesignDocId: function () {
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/index-results/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/actions.js b/app/addons/documents/index-results/actions.js
index 88d57bf..0f91295 100644
--- a/app/addons/documents/index-results/actions.js
+++ b/app/addons/documents/index-results/actions.js
@@ -74,7 +74,7 @@ function (app, FauxtonAPI, ActionTypes, Stores, HeaderStores, HeaderActions, Doc
reloadResultsList: function () {
return this.newResultsList({
collection: indexResultsStore.getCollection(),
- deleteable: indexResultsStore.isDeleteable()
+ isListDeletable: indexResultsStore.isListDeletable()
});
},
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/index-results/index-results.components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/index-results.components.react.jsx b/app/addons/documents/index-results/index-results.components.react.jsx
index 61d1955..0d0d8d1 100644
--- a/app/addons/documents/index-results/index-results.components.react.jsx
+++ b/app/addons/documents/index-results/index-results.components.react.jsx
@@ -43,7 +43,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
},
getUrlFragment: function (url) {
- if (this.props.hasReduce) {
+ if (!this.props.isEditable) {
return null;
}
@@ -55,6 +55,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
getDocumentList: function () {
return _.map(this.props.results, function (doc) {
+
return (
<Components.Document
key={doc.id}
@@ -64,6 +65,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
docContent={doc.content}
checked={this.props.isSelected(doc.id)}
docChecked={this.props.docChecked}
+ isDeletable={doc.isDeletable}
docIdentifier={doc.id} >
{this.getUrlFragment('#' + doc.url)}
</Components.Document>
@@ -79,7 +81,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
loadLines = <Components.LoadLines />;
}
- if (this.props.isDeleteable) {
+ if (this.props.isListDeletable) {
classNames += ' show-select';
}
@@ -111,10 +113,10 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
return {
hasResults: store.hasResults(),
results: store.getResults(),
- isDeleteable: store.isDeleteable(),
+ isListDeletable: store.isListDeletable(),
isSelected: store.isSelected,
- hasReduce: store.hasReduce(),
- isLoading: store.isLoading()
+ isLoading: store.isLoading(),
+ isEditable: store.isEditable()
};
},
@@ -149,8 +151,8 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
view = <ResultsScreen
isCollapsed={this.isCollapsed}
isSelected={this.isSelected}
- hasReduce={this.state.hasReduce}
- isDeleteable={this.state.isDeleteable}
+ isEditable={this.state.isEditable}
+ isListDeletable={this.state.isListDeletable}
docChecked={this.docChecked}
isLoading={this.state.isLoading}
results={this.state.results} />;
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/index-results/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/stores.js b/app/addons/documents/index-results/stores.js
index 605ee6f..52cf03e 100644
--- a/app/addons/documents/index-results/stores.js
+++ b/app/addons/documents/index-results/stores.js
@@ -27,7 +27,7 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
Stores.IndexResultsStore = FauxtonAPI.Store.extend({
initialize: function () {
- this._deleteable = false;
+ this._isListDeletable = false;
this._collection = [];
this.clearSelectedItems();
this.clearCollapsedDocs();
@@ -44,16 +44,29 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
newResults: function (options) {
this._collection = options.collection;
- this._deleteable = options.deleteable;
+ this._isListDeletable = options.isListDeletable;
this.clearSelectedItems();
this.clearCollapsedDocs();
},
- hasReduce: function () {
- if (!this._collection || !this._collection.params) {
+ isEditable: function (doc) {
+ if (!this._collection) {
return false;
}
- return this._collection.params.reduce;
+
+ if (!this._collection.isEditable) {
+ return false;
+ }
+
+ return this._collection.isEditable();
+ },
+
+ isDeletable: function (doc) {
+ return doc.isDeletable();
+ },
+
+ isListDeletable: function () {
+ return this._isListDeletable;
},
getCollection: function () {
@@ -79,7 +92,7 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
return doc.id;
}
- if (!_.isNull(doc.get('key'))) {
+ if (doc.get('key')) {
return doc.get('key').toString();
}
@@ -92,7 +105,9 @@ function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
content: this.getDocContent(doc),
id: this.getDocId(doc),
keylabel: doc.isFromView() ? 'key' : 'id',
- url: doc.isFromView() ? doc.url('app') : doc.url('web-index')
+ url: doc.isFromView() ? doc.url('app') : doc.url('web-index'),
+ isDeletable: this.isDeletable(doc),
+ isEditable: this.isEditable(doc),
};
}, this);
},
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/index-results/tests/index-results.actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.actionsSpec.js b/app/addons/documents/index-results/tests/index-results.actionsSpec.js
index 5ec9aab..1bbba77 100644
--- a/app/addons/documents/index-results/tests/index-results.actionsSpec.js
+++ b/app/addons/documents/index-results/tests/index-results.actionsSpec.js
@@ -153,6 +153,10 @@ define([
};
var stub = sinon.stub(store, 'createBulkDeleteFromSelected');
stub.returns(bulkDelete);
+ var reloadResultsListStub = sinon.stub(Actions, 'reloadResultsList');
+ var stubPromise = FauxtonAPI.Deferred();
+ stubPromise.resolve();
+ reloadResultsListStub.returns(stubPromise);
Actions.deleteSelected();
@@ -196,6 +200,10 @@ define([
};
var stub = sinon.stub(store, 'createBulkDeleteFromSelected');
stub.returns(bulkDelete);
+ var reloadResultsListStub = sinon.stub(Actions, 'reloadResultsList');
+ var stubPromise = FauxtonAPI.Deferred();
+ stubPromise.resolve();
+ reloadResultsListStub.returns(stubPromise);
Actions.deleteSelected();
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/index-results/tests/index-results.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.storesSpec.js b/app/addons/documents/index-results/tests/index-results.storesSpec.js
index 574082f..984d496 100644
--- a/app/addons/documents/index-results/tests/index-results.storesSpec.js
+++ b/app/addons/documents/index-results/tests/index-results.storesSpec.js
@@ -347,26 +347,22 @@ define([
});
});
- describe('hasReduce', function () {
+ describe('isEditable', function () {
it('returns false for no collection', function () {
store._collection = null;
- assert.notOk(store.hasReduce());
+ assert.notOk(store.isEditable());
});
- it('returns false for no params', function () {
+ it('returns false for empty collection', function () {
store._collection = [];
- assert.notOk(store.hasReduce());
+ assert.notOk(store.isEditable());
});
- it('returns true for reduce param', function () {
- store._collection = [];
- store._collection.param = {
- reduce: true
- };
- assert.notOk(store.hasReduce());
-
+ it('delegates to collection', function () {
+ store._collection = {};
+ store._collection.isEditable = function () { return 'stub'; };
+ assert.equal(store.isEditable(), 'stub');
});
-
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/mango/mango.actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.actions.js b/app/addons/documents/mango/mango.actions.js
new file mode 100644
index 0000000..d3df146
--- /dev/null
+++ b/app/addons/documents/mango/mango.actions.js
@@ -0,0 +1,58 @@
+// 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.
+
+define([
+ 'app',
+ 'api',
+ 'addons/documents/resources',
+ 'addons/documents/mango/mango.actiontypes',
+ 'addons/documents/mango/mango.stores',
+ 'addons/documents/index-results/actions'
+
+],
+function (app, FauxtonAPI, Documents, ActionTypes, Stores, IndexResultsActions) {
+ var store = Stores.mangoStore;
+
+ return {
+
+ setDatabase: function (options) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.MANGO_SET_DB,
+ options: options
+ });
+ },
+
+ saveQuery: function (options) {
+ var mangoIndex = new Documents.MangoIndex(JSON.parse(options.queryCode), {database: options.database});
+
+ FauxtonAPI.addNotification({
+ msg: 'Saving Index for Query...',
+ type: 'info',
+ clear: true
+ });
+
+ mangoIndex.save().then(function (res) {
+ var msg = res.result === 'created' ? 'Index created' : 'Index already exits',
+ url = FauxtonAPI.urls('mango', 'index-app', options.database.safeID());
+
+ FauxtonAPI.addNotification({
+ msg: msg,
+ type: 'success',
+ clear: true
+ });
+
+ IndexResultsActions.reloadResultsList();
+ }.bind(this));
+
+ }
+ };
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/mango/mango.actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.actiontypes.js b/app/addons/documents/mango/mango.actiontypes.js
new file mode 100644
index 0000000..112eacd
--- /dev/null
+++ b/app/addons/documents/mango/mango.actiontypes.js
@@ -0,0 +1,17 @@
+// 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.
+
+define([], function () {
+ return {
+ MANGO_SET_DB: 'MANGO_SET_DB',
+ };
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/mango/mango.components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.components.react.jsx b/app/addons/documents/mango/mango.components.react.jsx
new file mode 100644
index 0000000..8764779
--- /dev/null
+++ b/app/addons/documents/mango/mango.components.react.jsx
@@ -0,0 +1,156 @@
+// 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.
+
+define([
+ 'app',
+ 'api',
+ 'react',
+ 'addons/documents/mango/mango.stores',
+ 'addons/documents/mango/mango.actions',
+ 'addons/components/react-components.react',
+
+ 'plugins/prettify'
+],
+
+function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
+ var mangoStore = Stores.mangoStore;
+
+ var PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
+ var CodeEditor = ReactComponents.CodeEditor;
+ var ConfirmButton = ReactComponents.ConfirmButton;
+
+ var HelpScreen = React.createClass({
+ render: function () {
+ return (
+ <div className="watermark-logo">
+ <h3>{this.props.title}</h3>
+ <div>
+ Create an Index to query it afterwards.<br/><br/>
+ The example on the left shows how to create an index for the field '_id'
+ </div>
+ </div>
+ );
+ }
+ });
+
+ var MangoIndexEditorController = React.createClass({
+ getInitialState: function () {
+ return this.getStoreState();
+ },
+
+ getStoreState: function () {
+ return {
+ queryCode: mangoStore.getQueryCode(),
+ database: mangoStore.getDatabase(),
+ };
+ },
+
+ onChange: function () {
+ this.setState(this.getStoreState());
+ },
+
+ componentDidMount: function () {
+ mangoStore.on('change', this.onChange, this);
+ },
+
+ componentWillUnmount: function () {
+ mangoStore.off('change', this.onChange);
+ },
+
+ render: function () {
+ return (
+ <div className="editor-wrapper span5 scrollable">
+ <PaddedBorderedBox>
+ <div className="editor-description">{this.props.description}</div>
+ </PaddedBorderedBox>
+ <PaddedBorderedBox>
+ <strong>Database</strong>
+ <div className="db-title">{this.state.database.id}</div>
+ </PaddedBorderedBox>
+ <form className="form-horizontal" onSubmit={this.saveQuery}>
+ <PaddedBorderedBox>
+ <CodeEditor
+ id="query-field"
+ ref="indexQueryEditor"
+ title={'Index'}
+ docs={false}
+ code={this.state.queryCode} />
+ </PaddedBorderedBox>
+ <div className="padded-box">
+ <div className="control-group">
+ <ConfirmButton text="Create Index" />
+ </div>
+ </div>
+ </form>
+ </div>
+ );
+ },
+
+ getEditor: function () {
+ return this.refs.indexQueryEditor.getEditor();
+ },
+
+ hasValidCode: function () {
+ var editor = this.getEditor();
+ return editor.hadValidCode();
+ },
+
+ clearNotifications: function () {
+ var editor = this.getEditor();
+ editor.editSaved();
+ },
+
+ saveQuery: function (event) {
+ event.preventDefault();
+
+ if (!this.hasValidCode()) {
+ FauxtonAPI.addNotification({
+ msg: 'Please fix the Javascript errors and try again.',
+ type: 'error',
+ clear: true
+ });
+ return;
+ }
+
+ this.clearNotifications();
+
+ Actions.saveQuery({
+ database: this.state.database,
+ queryCode: this.refs.indexQueryEditor.getValue()
+ });
+ }
+ });
+
+ var Views = {
+ renderHelpScreen: function (el) {
+ React.render(
+ <HelpScreen title={app.i18n.en_US['mango-help-title']} />,
+ el
+ );
+ },
+ removeHelpScreen: function (el) {
+ React.unmountComponentAtNode(el);
+ },
+ renderMangoIndexEditor: function (el) {
+ React.render(
+ <MangoIndexEditorController description={app.i18n.en_US['mango-descripton']} />,
+ el
+ );
+ },
+ removeMangoIndexEditor: function (el) {
+ React.unmountComponentAtNode(el);
+ },
+ MangoIndexEditorController: MangoIndexEditorController
+ };
+
+ return Views;
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/mango/mango.stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.stores.js b/app/addons/documents/mango/mango.stores.js
new file mode 100644
index 0000000..f72a046
--- /dev/null
+++ b/app/addons/documents/mango/mango.stores.js
@@ -0,0 +1,63 @@
+// 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.
+
+define([
+ 'api',
+ 'addons/documents/mango/mango.actiontypes'
+],
+
+function (FauxtonAPI, ActionTypes) {
+
+
+ var defaultQuery = '{\n' +
+ ' "index": {\n' +
+ ' "fields": ["_id"]\n' +
+ ' },\n' +
+ ' "type" : "json"\n' +
+ '}';
+
+ var Stores = {};
+
+ Stores.MangoStore = FauxtonAPI.Store.extend({
+
+ getQueryCode: function () {
+ return defaultQuery;
+ },
+
+ setDatabase: function (options) {
+ this._database = options.database;
+ },
+
+ getDatabase: function () {
+ return this._database;
+ },
+
+ dispatch: function (action) {
+ switch (action.type) {
+
+ case ActionTypes.MANGO_SET_DB:
+ this.setDatabase(action.options);
+ this.triggerChange();
+ break;
+
+ }
+ }
+
+ });
+
+ Stores.mangoStore = new Stores.MangoStore();
+
+ Stores.mangoStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.mangoStore.dispatch);
+
+ return Stores;
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx b/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
new file mode 100644
index 0000000..c50897b
--- /dev/null
+++ b/app/addons/documents/mango/tests/mango.componentsSpec.react.jsx
@@ -0,0 +1,93 @@
+// 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.
+
+define([
+ 'api',
+ 'addons/documents/mango/mango.components.react',
+ 'addons/documents/mango/mango.stores',
+ 'addons/documents/mango/mango.actions',
+
+ 'addons/documents/resources',
+ 'addons/databases/resources',
+
+ 'testUtils',
+ 'react'
+], function (FauxtonAPI, Views, Stores, MangoActions, Resources, Databases, utils, React) {
+
+ var assert = utils.assert;
+ var TestUtils = React.addons.TestUtils;
+
+ var fakeData = [
+ {
+ ddoc: '_design/e4d338e5d6f047749f5399ab998b4fa04ba0c816',
+ def: {
+ fields: [{
+ '_id': 'asc'
+ }]
+ },
+ name: 'e4d338e5d6f047749f5399ab998b4fa04ba0c816',
+ type: 'json'
+ },
+ {
+ ddoc: null,
+ def: {
+ fields: [{
+ '_id': 'asc'
+ }]
+ },
+ name: '_all_docs',
+ type: 'special'
+ }
+ ];
+
+
+ describe('Mango IndexEditor', function () {
+ var database = new Databases.Model({id: 'testdb'}),
+ container,
+ editor;
+
+ beforeEach(function () {
+ container = document.createElement('div');
+ MangoActions.setDatabase({
+ database: database
+ });
+ $('body').append('<div id="query-field"></div>');
+ });
+
+ afterEach(function () {
+ React.unmountComponentAtNode(container);
+ $('#query-field').remove();
+ });
+
+ it('renders a default index definition', function () {
+ editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="foo" />, container);
+ var $el = $(editor.getDOMNode());
+ var payload = JSON.parse($el.find('.js-editor').text());
+ assert.equal(payload.index.fields[0], '_id');
+ });
+
+ it('renders the current database', function () {
+ editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="foo" />, container);
+ var $el = $(editor.getDOMNode());
+
+ assert.equal($el.find('.db-title').text(), 'testdb');
+ });
+
+ it('renders a description', function () {
+ editor = TestUtils.renderIntoDocument(<Views.MangoIndexEditorController description="CouchDB Query is great!" />, container);
+ var $el = $(editor.getDOMNode());
+
+ assert.equal($el.find('.editor-description').text(), 'CouchDB Query is great!');
+ });
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js
index e6ee676..00559b9 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -72,6 +72,73 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
}
});
+ Documents.MangoIndex = Documents.Doc.extend({
+ idAttribute: 'name',
+
+ isNew: function () {
+ // never use put
+ return true;
+ },
+
+ isDeletable: function () {
+ return this.get('type') !== 'special';
+ },
+
+ 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);
+ },
+
+ url: function () {
+ return this.urlRef.apply(this, arguments);
+ },
+
+ updateSeq: function () {
+ return false;
+ },
+
+ isEditable: function () {
+ return false;
+ },
+
+ parse: function (res) {
+ return res.indexes;
+ },
+
+ urlRef: function (params) {
+ var database = this.database.safeID(),
+ 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);
+ }
+
+ return FauxtonAPI.urls('mango', 'index-apiurl', database, query);
+ }
+ });
+
Documents.NewDoc = Documents.Doc.extend({
fetch: function () {
var uuid = new FauxtonAPI.UUID();
@@ -206,6 +273,10 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
}
},
+ isEditable: function () {
+ return !this.params.reduce;
+ },
+
urlRef: function (params) {
var query = "";
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index ff57dac..2cf8907 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -20,6 +20,7 @@ define([
'addons/documents/views-changes',
'addons/documents/views-index',
'addons/documents/views-doceditor',
+ 'addons/documents/views-mango',
'addons/databases/base',
'addons/documents/resources',
@@ -28,7 +29,7 @@ define([
'addons/documents/index-results/actions'
],
-function (app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor,
+function (app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor, Mango,
Databases, Resources, Components, PaginationStores, IndexResultsActions) {
@@ -44,6 +45,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor,
roles: ['fx_loggedIn']
},
'database/:database/_changes': 'changes'
+
},
events: {
@@ -77,29 +79,6 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor,
this.addSidebar();
},
- getAllDatabases: function () {
- return new Databases.List(); //getAllDatabases() can be overwritten instead of hard coded into initViews
- },
-
- // this safely assumes the db name is valid
- onSelectDatabase: function (dbName) {
- this.cleanup();
- this.initViews(dbName);
-
- var url = FauxtonAPI.urls('allDocs', 'app', app.utils.safeURLName(dbName), '');
- FauxtonAPI.navigate(url, {
- trigger: true
- });
-
- // we need to start listening again because cleanup() removed the listener, but in this case
- // initialize() doesn't fire to re-set up the listener
- this.listenToLookaheadTray();
- },
-
- listenToLookaheadTray: function () {
- this.listenTo(FauxtonAPI.Events, 'lookaheadTray:update', this.onSelectDatabase);
- },
-
designDocMetadata: function (database, ddoc) {
this.footer && this.footer.remove();
this.toolsView && this.toolsView.remove();
@@ -159,7 +138,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor,
IndexResultsActions.newResultsList({
collection: collection,
- deleteable: true
+ isListDeletable: true
});
this.database.allDocs.paging.pageSize = PaginationStores.indexPaginationStore.getPerPage();
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/routes-index-editor.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-index-editor.js b/app/addons/documents/routes-index-editor.js
index 38c9566..4d1e3b3 100644
--- a/app/addons/documents/routes-index-editor.js
+++ b/app/addons/documents/routes-index-editor.js
@@ -92,7 +92,7 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Documents, Index,
IndexResultsActions.newResultsList({
collection: this.indexedDocs,
- deleteable: false
+ isListDeletable: false
});
this.viewEditor = this.setView('#left-content', new Index.ViewEditorReact({
@@ -142,7 +142,7 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Documents, Index,
this.resultList = this.setView('#dashboard-lower-content', new Index.ViewResultListReact({}));
IndexResultsActions.newResultsList({
collection: [],
- deleteable: false
+ isListDeletable: false
});
}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/routes-mango.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-mango.js b/app/addons/documents/routes-mango.js
new file mode 100644
index 0000000..baade58
--- /dev/null
+++ b/app/addons/documents/routes-mango.js
@@ -0,0 +1,154 @@
+// 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.
+
+define([
+ 'app',
+ 'api',
+
+ // Modules
+ 'addons/documents/helpers',
+ 'addons/documents/shared-routes',
+ 'addons/documents/views-mango',
+ 'addons/databases/resources',
+ 'addons/fauxton/components',
+ 'addons/documents/resources',
+ 'addons/documents/views',
+
+
+ 'addons/documents/index-results/actions',
+ 'addons/documents/pagination/stores',
+
+],
+
+function (app, FauxtonAPI, Helpers, BaseRoute, Mango, Databases,
+ Components, Resources, Documents, IndexResultsActions, PaginationStores) {
+
+ var MangoIndexList = BaseRoute.extend({
+ layout: 'with_tabs_sidebar',
+ routes: {
+ 'database/:database/_indexlist(:extra)': {
+ route: 'mangoIndexList',
+ roles: ['fx_loggedIn']
+ },
+
+ },
+
+ establish: function () {
+ return [
+ this.designDocs.fetch({reset: true}),
+ this.allDatabases.fetchOnce()
+ ];
+ },
+
+ initialize: function (route, masterLayout, options) {
+ var databaseName = options[0];
+ this.databaseName = databaseName;
+ this.database = new Databases.Model({id: databaseName});
+
+ // magic methods
+ this.allDatabases = this.getAllDatabases();
+ this.createDesignDocsCollection();
+ this.addLeftHeader();
+ this.addSidebar();
+
+ this.rightHeader = this.setView('#right-header', new Documents.Views.RightAllDocsHeader({
+ database: this.database
+ }));
+ },
+
+ mangoIndexList: function () {
+ var params = this.createParams(),
+ urlParams = params.urlParams,
+ mangoIndexCollection = new Resources.MangoIndexCollection(null, {
+ database: this.database,
+ params: null,
+ paging: {
+ pageSize: PaginationStores.indexPaginationStore.getPerPage()
+ }
+ });
+
+ this.viewEditor && this.viewEditor.remove();
+ this.headerView && this.headerView.remove();
+
+ this.sidebar.setSelectedTab('mango-indexes');
+
+ IndexResultsActions.newResultsList({
+ collection: mangoIndexCollection,
+ isListDeletable: false
+ });
+
+ this.reactHeader = this.setView('#react-headerbar', new Documents.Views.ReactHeaderbar());
+
+ this.leftheader.updateCrumbs(this.getCrumbs(this.database));
+ this.rightHeader.hideQueryOptions();
+
+ this.resultList = this.setView('#dashboard-lower-content', new Mango.MangoIndexListReact());
+
+ this.apiUrl = function () {
+ return [mangoIndexCollection.urlRef(urlParams), FauxtonAPI.constants.DOC_URLS.GENERAL];
+ };
+ }
+ });
+
+ var MangoIndexEditorAndResults = BaseRoute.extend({
+ layout: 'two_pane',
+ routes: {
+ 'database/:database/_index': {
+ route: 'createIndex',
+ roles: ['fx_loggedIn']
+ }
+ },
+
+ initialize: function (route, masterLayout, options) {
+ var databaseName = options[0];
+
+ this.databaseName = databaseName;
+ this.database = new Databases.Model({id: databaseName});
+ },
+
+ createIndex: function (database) {
+ var params = this.createParams(),
+ urlParams = params.urlParams,
+ mangoIndexCollection = new Resources.MangoIndexCollection(null, {
+ database: this.database
+ });
+
+ IndexResultsActions.newResultsList({
+ collection: mangoIndexCollection,
+ isListDeletable: false
+ });
+
+ this.breadcrumbs = this.setView('#breadcrumbs', new Components.Breadcrumbs({
+ toggleDisabled: true,
+ crumbs: [
+ {'type': 'back', 'link': Helpers.getPreviousPage(this.database)},
+ {'name': 'Create new index', 'link': Databases.databaseUrl(this.database) }
+ ]
+ }));
+
+ this.resultList = this.setView('#dashboard-lower-content', new Mango.HelpScreen());
+
+ this.mangoEditor = this.setView('#left-content', new Mango.MangoIndexEditorReact({
+ database: this.database
+ }));
+
+ this.apiUrl = function () {
+ return [mangoIndexCollection.urlRef(urlParams), FauxtonAPI.constants.DOC_URLS.GENERAL];
+ };
+ }
+ });
+
+ return {
+ MangoIndexEditorAndResults: MangoIndexEditorAndResults,
+ MangoIndexList: MangoIndexList
+ };
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes.js b/app/addons/documents/routes.js
index 0f16d4a..ad92ded 100644
--- a/app/addons/documents/routes.js
+++ b/app/addons/documents/routes.js
@@ -14,16 +14,19 @@ define([
"addons/documents/views",
"addons/documents/routes-documents",
'addons/documents/routes-doc-editor',
- 'addons/documents/routes-index-editor'
+ 'addons/documents/routes-index-editor',
+ 'addons/documents/routes-mango'
],
-function (Documents, DocumentsRouteObject, docEditor, IndexEditorRouteObject) {
+function (Documents, DocumentsRouteObject, docEditor, IndexEditorRouteObject, Mango) {
Documents.RouteObjects = [
docEditor.DocEditorRouteObject,
docEditor.NewDocEditorRouteObject,
DocumentsRouteObject,
- IndexEditorRouteObject
+ IndexEditorRouteObject,
+ Mango.MangoIndexList,
+ Mango.MangoIndexEditorAndResults
];
return Documents;
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/shared-resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-resources.js b/app/addons/documents/shared-resources.js
index 04098c0..4800480 100644
--- a/app/addons/documents/shared-resources.js
+++ b/app/addons/documents/shared-resources.js
@@ -57,6 +57,10 @@ define([
return this.id && this.id.match(/^_design\//) ? "design doc" : "doc";
},
+ isDeletable: function () {
+ return true;
+ },
+
isFromView: function () {
return !this.id;
},
@@ -215,6 +219,10 @@ define([
}
},
+ isEditable: function () {
+ return true;
+ },
+
urlRef: function (context, params) {
var query = "";
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/shared-routes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-routes.js b/app/addons/documents/shared-routes.js
index 4766203..9a59afd 100644
--- a/app/addons/documents/shared-routes.js
+++ b/app/addons/documents/shared-routes.js
@@ -43,6 +43,28 @@ define([
});
},
+ onSelectDatabase: function (dbName) {
+ this.cleanup();
+ this.initViews(dbName);
+
+ var url = FauxtonAPI.urls('allDocs', 'app', app.utils.safeURLName(dbName), '');
+ FauxtonAPI.navigate(url, {
+ trigger: true
+ });
+
+ // we need to start listening again because cleanup() removed the listener, but in this case
+ // initialize() doesn't fire to re-set up the listener
+ this.listenToLookaheadTray();
+ },
+
+ listenToLookaheadTray: function () {
+ this.listenTo(FauxtonAPI.Events, 'lookaheadTray:update', this.onSelectDatabase);
+ },
+
+ getAllDatabases: function () {
+ return new Databases.List(); //getAllDatabases() can be overwritten instead of hard coded into initViews
+ },
+
showQueryOptions: function (urlParams, ddoc, viewName) {
var promise = this.designDocs.fetch({reset: true}),
that = this,
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/shared-views.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-views.js b/app/addons/documents/shared-views.js
index 93876a7..627c682 100644
--- a/app/addons/documents/shared-views.js
+++ b/app/addons/documents/shared-views.js
@@ -118,6 +118,11 @@ function (app, FauxtonAPI, Components, Documents, Databases) {
this.designDocList = [];
this.collection.each(function (design) {
+
+ if (design.get('doc').language === 'query') {
+ return;
+ }
+
if (design.has('doc')) {
design.collection = this.collection;
var view = this.insertView(new Views.DdocSidenav({
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/tests/headerSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/headerSpec.react.jsx b/app/addons/documents/tests/headerSpec.react.jsx
index 33528c5..420aebe 100644
--- a/app/addons/documents/tests/headerSpec.react.jsx
+++ b/app/addons/documents/tests/headerSpec.react.jsx
@@ -83,7 +83,7 @@ define([
IndexResultsActions.newResultsList({
collection: database.allDocs,
- deleteable: false
+ isListDeletable: false
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/tests/nightwatch/mangoIndex.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/mangoIndex.js b/app/addons/documents/tests/nightwatch/mangoIndex.js
new file mode 100644
index 0000000..1c66ebe
--- /dev/null
+++ b/app/addons/documents/tests/nightwatch/mangoIndex.js
@@ -0,0 +1,48 @@
+// 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.
+
+module.exports = {
+
+ 'Creating new indexes with mango': function (client) {
+ /*jshint multistr: true */
+ var waitTime = 10000,
+ newDatabaseName = client.globals.testDatabaseName,
+ baseUrl = client.globals.test_settings.launch_url;
+
+ client
+ .populateDatabase(newDatabaseName)
+ .loginToGUI()
+ .url(baseUrl + '/#/database/' + newDatabaseName + '/_index')
+ .waitForElementPresent('.watermark-logo', waitTime, false)
+ .assert.containsText('.watermark-logo', 'Mango')
+ .assert.containsText('.editor-description', 'is an easy way to find documents on predefined indexes')
+ .execute('\
+ var json = \'{\
+ "index": {\
+ "fields": ["ente_ente_mango"]\
+ },\
+ "name": "rocko-artischocko",\
+ "type" : "json"\
+ }\';\
+ var editor = ace.edit("query-field");\
+ editor.getSession().setValue(json);\
+ ')
+ .execute('$(".save")[0].scrollIntoView();')
+ .click('button.btn-success.save')
+
+ .waitForElementNotVisible('.global-notification', waitTime, false)
+ .url(baseUrl + '/#/database/' + newDatabaseName + '/_indexlist')
+ .waitForElementPresent('.prettyprint', waitTime, false)
+ .assert.containsText('#dashboard-lower-content', 'ente_ente_mango')
+ .end();
+ }
+};
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/tests/nightwatch/mangoIndexList.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/mangoIndexList.js b/app/addons/documents/tests/nightwatch/mangoIndexList.js
new file mode 100644
index 0000000..a4f8cc0
--- /dev/null
+++ b/app/addons/documents/tests/nightwatch/mangoIndexList.js
@@ -0,0 +1,29 @@
+// 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.
+
+module.exports = {
+
+ 'Creating new indexes with mango': function (client) {
+ var waitTime = 10000,
+ newDatabaseName = client.globals.testDatabaseName,
+ baseUrl = client.globals.test_settings.launch_url;
+
+ client
+ .populateDatabase(newDatabaseName)
+ .loginToGUI()
+ .url(baseUrl + '/#/database/' + newDatabaseName + '/_indexlist')
+ .waitForElementPresent('.prettyprint', waitTime, false)
+ .assert.containsText('.header-doc-id', '_all_docs')
+ .assert.containsText('#doc-list', 'ente_ente_mango_ananas')
+ .end();
+ }
+};
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/tests/resourcesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/resourcesSpec.js b/app/addons/documents/tests/resourcesSpec.js
index 11181ef..8d0e0c8 100644
--- a/app/addons/documents/tests/resourcesSpec.js
+++ b/app/addons/documents/tests/resourcesSpec.js
@@ -71,21 +71,110 @@ define([
});
});
+ describe('MangoIndex', function () {
+ var doc;
+
+ it('is deleteable', function () {
+ var index = {
+ ddoc: null,
+ name: '_all_docs',
+ type: 'json',
+ def: {fields: [{_id: 'asc'}]}
+ };
+ doc = new Models.MangoIndex(index, {});
+
+ assert.ok(doc.isDeletable());
+ });
+
+ it('special docs are not deleteable', function () {
+ var index = {
+ ddoc: null,
+ name: '_all_docs',
+ type: 'special',
+ def: {fields: [{_id: 'asc'}]}
+ };
+ doc = new Models.MangoIndex(index, {});
+
+ assert.notOk(doc.isDeletable());
+ });
+ });
+
+ describe('MangoIndexCollection', function () {
+ var collection;
+
+ it('is not editable', function () {
+ collection = new Models.MangoIndexCollection([{
+ name: 'myId1',
+ doc: 'num1'
+ },
+ {
+ name: 'myId2',
+ doc: 'num2'
+ }], {
+ database: {id: 'databaseId', safeID: function () { return this.id; }},
+ params: {limit: 20}
+ });
+
+ assert.notOk(collection.isEditable());
+ });
+ });
+
+
+ describe('IndexCollection', function () {
+ var collection;
+
+ it('design docs are editable', function () {
+ collection = new Models.IndexCollection([{
+ _id: 'myId1',
+ doc: 'num1'
+ },
+ {
+ _id: 'myId2',
+ doc: 'num2'
+ }], {
+ database: {id: 'databaseId', safeID: function () { return this.id; }},
+ params: {limit: 20},
+ design: '_design/foobar'
+ });
+
+ assert.ok(collection.isEditable());
+ });
+
+ it('reduced design docs are NOT editable', function () {
+ collection = new Models.IndexCollection([{
+ _id: 'myId1',
+ doc: 'num1'
+ },
+ {
+ _id: 'myId2',
+ doc: 'num2'
+ }], {
+ database: {id: 'databaseId', safeID: function () { return this.id; }},
+ params: {limit: 20, reduce: true},
+ design: '_design/foobar'
+ });
+
+ assert.notOk(collection.isEditable());
+ });
+ });
+
describe('AllDocs', function () {
var collection;
- beforeEach(function () {
+
+ it('all-docs-list documents are always editable', function () {
collection = new Models.AllDocs([{
- _id:'myId1',
+ _id: 'myId1',
doc: 'num1'
},
{
- _id:'myId2',
+ _id: 'myId2',
doc: 'num2'
}], {
database: {id: 'databaseId', safeID: function () { return this.id; }},
params: {limit: 20}
});
+ assert.ok(collection.isEditable());
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx b/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
index 659cc8e..68c4033 100644
--- a/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
+++ b/app/addons/documents/tests/viewIndex.componentsSpec.react.jsx
@@ -23,8 +23,20 @@ define([
var assert = utils.assert;
var TestUtils = React.addons.TestUtils;
- var resetStore = function (designDoc) {
- var designDocs = new Documents.AllDocs([designDoc], {
+ var resetStore = function (designDocs) {
+ designDocs = designDocs.map(function (doc) {
+ return Documents.Doc.prototype.parse(doc);
+ });
+
+ designDocs.map(function (ddoc) {
+ return new Documents.Doc(ddoc, {
+ database: {
+ safeID: function () { return 'id'; }
+ }
+ });
+ });
+
+ var ddocs = new Documents.AllDocs(designDocs, {
params: { limit: 10 },
database: {
safeID: function () { return 'id';}
@@ -35,8 +47,8 @@ define([
database: {id: 'rockos-db'},
newView: false,
viewName: 'test-view',
- designDocs: designDocs,
- designDocId: designDoc._id
+ designDocs: ddocs,
+ designDocId: designDocs[0]._id
});
};
@@ -72,7 +84,7 @@ define([
}
};
- resetStore(designDoc);
+ resetStore([designDoc]);
reduceEl = TestUtils.renderIntoDocument(<Views.ReduceEditor/>, container);
assert.ok(_.isNull(reduceEl.getReduceValue()));
@@ -91,7 +103,7 @@ define([
}
};
- resetStore(designDoc);
+ resetStore([designDoc]);
reduceEl = TestUtils.renderIntoDocument(<Views.ReduceEditor/>, container);
assert.equal(reduceEl.getReduceValue(), '_sum');
@@ -107,6 +119,61 @@ define([
container = document.createElement('div');
$('body').append('<div id="map-function"></div>');
$('body').append('<div id="editor"></div>');
+ var designDoc = {
+ "id": "_design/test-doc",
+ "key": "_design/test-doc",
+ "value": {
+ "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
+ },
+ "doc": {
+ "_id": "_design/test-doc",
+ "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
+ "views": {
+ "test-view": {
+ "map": "function(doc) {\n emit(doc._id, 2);\n}"
+ },
+ "new-view": {
+ "map": "function(doc) {\n if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n emit(doc._id, 1);\n}",
+ "reduce": "_sum"
+ }
+ },
+ "language": "javascript",
+ "indexes": {
+ "newSearch": {
+ "analyzer": "standard",
+ "index": "function(doc){\n index(\"default\", doc._id);\n}"
+ }
+ }
+ }
+ };
+ var mangodoc = {
+ "id": "_design/123mango",
+ "key": "_design/123mango",
+ "value": {
+ "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
+ },
+ "doc": {
+ "_id": "_design/123mango",
+ "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
+ "views": {
+ "test-view": {
+ "map": "function(doc) {\n emit(doc._id, 2);\n}"
+ },
+ "new-view": {
+ "map": "function(doc) {\n if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n emit(doc._id, 1);\n}",
+ "reduce": "_sum"
+ }
+ },
+ "language": "query",
+ "indexes": {
+ "newSearch": {
+ "analyzer": "standard",
+ "index": "function(doc){\n index(\"default\", doc._id);\n}"
+ }
+ }
+ }
+ };
+ resetStore([designDoc, mangodoc]);
selectorEl = TestUtils.renderIntoDocument(<Views.DesignDocSelector/>, container);
});
@@ -151,6 +218,14 @@ define([
assert.ok(spy.calledWith('_design/new-doc-entered', true));
});
+ it('does not filter usual design docs', function () {
+ assert.ok(/_design\/test-doc/.test($(selectorEl.getDOMNode()).text()));
+ });
+
+ it('filters mango docs', function () {
+ selectorEl = TestUtils.renderIntoDocument(<Views.DesignDocSelector/>, container);
+ assert.notOk(/_design\/123mango/.test($(selectorEl.getDOMNode()).text()));
+ });
});
describe('Editor', function () {
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/addons/documents/views-mango.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views-mango.js b/app/addons/documents/views-mango.js
new file mode 100644
index 0000000..7b45984
--- /dev/null
+++ b/app/addons/documents/views-mango.js
@@ -0,0 +1,66 @@
+// 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.
+
+define([
+ 'api',
+ 'addons/documents/mango/mango.components.react',
+ 'addons/documents/mango/mango.actions',
+ 'addons/documents/index-results/index-results.components.react'
+],
+
+function (FauxtonAPI, Mango, MangoActions, ViewResultList) {
+
+ var Views = {};
+
+
+ Views.HelpScreen = FauxtonAPI.View.extend({
+
+ afterRender: function () {
+ Mango.renderHelpScreen(this.el);
+ },
+
+ cleanup: function () {
+ Mango.removeHelpScreen(this.el);
+ }
+ });
+
+ Views.MangoIndexListReact = FauxtonAPI.View.extend({
+
+ afterRender: function () {
+ ViewResultList.renderViewResultList(this.el);
+ },
+
+ cleanup: function () {
+ ViewResultList.removeViewResultList(this.el);
+ }
+ });
+
+ Views.MangoIndexEditorReact = FauxtonAPI.View.extend({
+ initialize: function (options) {
+ this.database = options.database;
+ },
+
+ afterRender: function () {
+ MangoActions.setDatabase({
+ database: this.database
+ });
+
+ Mango.renderMangoIndexEditor(this.el);
+ },
+
+ cleanup: function () {
+ Mango.removeMangoIndexEditor(this.el);
+ }
+ });
+
+ return Views;
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/app/initialize.js.underscore
----------------------------------------------------------------------
diff --git a/app/initialize.js.underscore b/app/initialize.js.underscore
index c0af5fb..0f6c36f 100644
--- a/app/initialize.js.underscore
+++ b/app/initialize.js.underscore
@@ -26,7 +26,8 @@ function () {
version: "<%= version %>",
// Host is used as prefix for urls
host: "<%= host %>",
- zeroClipboardPath: "<%= zeroClipboardPath %>"
+ zeroClipboardPath: "<%= zeroClipboardPath %>",
+ i18n: <%= i18n %>
};
return app;
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/i18n.json.default
----------------------------------------------------------------------
diff --git a/i18n.json.default b/i18n.json.default
new file mode 100644
index 0000000..aa89b17
--- /dev/null
+++ b/i18n.json.default
@@ -0,0 +1,8 @@
+{
+ "en_US": {
+ "mango-descripton": "Mango is an easy way to find documents on predefined indexes.",
+ "all-mango-indexes": "All Mango Indexes",
+ "new-mango-index": "New Mango Index",
+ "mango-help-title": "Mango"
+ }
+}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index e9b13ea..cd8efe1 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"nano": "~5.12.0",
"nightwatch": "~0.5.33",
"react-tools": "^0.12.0",
+ "request": "^2.54.0",
"send": "~0.1.1",
"underscore": "~1.4.2",
"url": "~0.7.9",
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/tasks/fauxton.js
----------------------------------------------------------------------
diff --git a/tasks/fauxton.js b/tasks/fauxton.js
index 743c8ef..ee73399 100644
--- a/tasks/fauxton.js
+++ b/tasks/fauxton.js
@@ -92,12 +92,11 @@ module.exports = function (grunt) {
grunt.registerMultiTask('gen_initialize', 'Generate the app.js file', function () {
var _ = grunt.util._,
- settings = this.data,
- template = "app/initialize.js.underscore",
- dest = "app/initialize.js",
- tmpl = _.template(grunt.file.read(template)),
- app = {};
-
+ settings = this.data,
+ template = "app/initialize.js.underscore",
+ dest = "app/initialize.js",
+ tmpl = _.template(grunt.file.read(template)),
+ app = {};
_.defaults(app, settings.app, {
root: '/',
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/tasks/helper.js
----------------------------------------------------------------------
diff --git a/tasks/helper.js b/tasks/helper.js
index e0b82bb..abfc01f 100644
--- a/tasks/helper.js
+++ b/tasks/helper.js
@@ -28,6 +28,17 @@ exports.init = function (grunt) {
}
},
+ readI18nFile: function () {
+ if (fs.existsSync('i18n.json')) {
+ return grunt.file.readJSON('i18n.json');
+ }
+ if (fs.existsSync('i18n.json.default')) {
+ return grunt.file.readJSON('i18n.json.default');
+ }
+
+ throw new Error('i18n file missing');
+ },
+
processAddons: function (callback) {
this.readSettingsFile().deps.forEach(callback);
},
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/d7641f16/test/nightwatch_tests/custom-commands/populateDatabase.js
----------------------------------------------------------------------
diff --git a/test/nightwatch_tests/custom-commands/populateDatabase.js b/test/nightwatch_tests/custom-commands/populateDatabase.js
index f17df8f..b5cd161 100644
--- a/test/nightwatch_tests/custom-commands/populateDatabase.js
+++ b/test/nightwatch_tests/custom-commands/populateDatabase.js
@@ -13,7 +13,8 @@
var util = require('util'),
events = require('events'),
helpers = require('../helpers/helpers.js'),
- async = require('async');
+ async = require('async'),
+ request = require('request');
function PopulateDatabase () {
events.EventEmitter.call(this);
@@ -56,7 +57,9 @@ PopulateDatabase.prototype.command = function (databaseName, count) {
createKeyView(null, function () {
createBrokenView(null, function () {
- that.emit('complete');
+ createMangoIndex(null, function () {
+ that.emit('complete');
+ });
});
});
});
@@ -97,6 +100,29 @@ PopulateDatabase.prototype.command = function (databaseName, count) {
cb();
});
}
+
+ function createMangoIndex (err, cb) {
+ request({
+ uri: helpers.test_settings.db_url + '/' + databaseName + '/_index',
+ method: 'POST',
+ json: true,
+ body: {
+ index: {
+ fields: ['ente_ente_mango_ananas']
+ },
+ name: 'rocko-artischockbert',
+ type: 'json'
+ }
+ }, function (err, res, body) {
+ if (err) {
+ console.log('Error in nano populateDatabase Function: ' +
+ err.message);
+ }
+
+ cb && cb();
+ });
+ }
+
return this;
};