You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by kx...@apache.org on 2015/12/21 22:42:50 UTC

[1/2] couchdb commit: updated refs/heads/master to 37f4d55

Repository: couchdb
Updated Branches:
  refs/heads/master bdc9681fd -> 37f4d5588


Rewrite via query server

COUCHDB-2874


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/101e5416
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/101e5416
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/101e5416

Branch: refs/heads/master
Commit: 101e54169b7b9ba389514a6dc00dc7ee401e5566
Parents: bdc9681
Author: Alexander Shorin <kx...@apache.org>
Authored: Wed Nov 18 21:39:21 2015 +0300
Committer: Alexander Shorin <kx...@apache.org>
Committed: Tue Dec 22 00:41:31 2015 +0300

----------------------------------------------------------------------
 rebar.config.script                   |   6 +-
 share/server/loop.js                  |   3 +-
 share/server/render.js                |  26 ++-
 test/javascript/tests/rewrite_js.js   | 340 +++++++++++++++++++++++++++++
 test/view_server/query_server_spec.rb |  61 ++++++
 5 files changed, 430 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/101e5416/rebar.config.script
----------------------------------------------------------------------
diff --git a/rebar.config.script b/rebar.config.script
index 324957e..ecb773b 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -30,10 +30,10 @@ DepDescs = [
 {couch_log,        "couch-log",        "939b3a7bda7dcb03f841b899762b188ca31bc230"},
 {couch_log_lager,  "couch-log-lager",  "b2a0471a87765de50c5eb05c65c121f68a9ae9fa"},
 {config,           "config",           "84197a6f1c5cb43447239df1fe57b4312b0c03c4"},
-{chttpd,           "chttpd",           "d31b2a4d44d68f8e9a1ac2c55c47e0cf4e95397b"},
-{couch,            "couch",            "57ecc0427c465156b0b10111737f32ae2242391e"},
+{chttpd,           "chttpd",           "417679a9cf2277693253d3f9c2ac0e52fa1ba75c"},
+{couch,            "couch",            "f33f9fa1281fd6c50a86cb9853bd2d9ec2dd007c"},
 {couch_index,      "couch-index",      "14f579dcd142ee90300244c854b301bbd5c863ee"},
-{couch_mrview,     "couch-mrview",     "c3bed460ee844175b8ce11081386be27f686d8ff"},
+{couch_mrview,     "couch-mrview",     "6c9833d667e319b82a7a1fffb8ba92116534e63f"},
 {couch_replicator, "couch-replicator", "3f268abba89bd5b93f43185465e66ef42b3876ad"},
 {couch_plugins,    "couch-plugins",    "3e73b723cb126cfc471b560d17c24a8b5c540085"},
 {couch_event,      "couch-event",      "835a41885d1e276d207758954f8238aa7bba0ae8"},

http://git-wip-us.apache.org/repos/asf/couchdb/blob/101e5416/share/server/loop.js
----------------------------------------------------------------------
diff --git a/share/server/loop.js b/share/server/loop.js
index e1226c3..551004a 100644
--- a/share/server/loop.js
+++ b/share/server/loop.js
@@ -62,7 +62,8 @@ var DDoc = (function() {
     "filters"   : Filter.filter,
     "views"     : Filter.filter_view, 
     "updates"  : Render.update,
-    "validate_doc_update" : Validate.validate
+    "validate_doc_update" : Validate.validate,
+    "rewrites"  : Render.rewrite
   };
   var ddocs = {};
   return {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/101e5416/share/server/render.js
----------------------------------------------------------------------
diff --git a/share/server/render.js b/share/server/render.js
index 49b0863..1fe4da2 100644
--- a/share/server/render.js
+++ b/share/server/render.js
@@ -314,7 +314,26 @@ var Render = (function() {
     }
   };
 
-  function renderError(e, funSrc) {
+  function runRewrite(fun, ddoc, args) {
+      var result;
+      try {
+        result = fun.apply(ddoc, args);
+      } catch(error) {
+        renderError(error, fun.toString(), "rewrite_error");
+      }
+
+      if (!result) {
+        respond(["no_dispatch_rule"]);
+        return;
+      }
+
+      if (typeof result === "string") {
+        result = {path: result, method: args[0].method};
+      }
+      respond(["ok", result]);
+  }
+
+  function renderError(e, funSrc, errType) {
     if (e.error && e.reason || e[0] == "error" || e[0] == "fatal") {
       throw(e);
     } else {
@@ -322,7 +341,7 @@ var Render = (function() {
                        (e.toSource ? e.toSource() : e.toString()) + " \n" +
                        "stacktrace: " + e.stack;
       log(logMessage);
-      throw(["error", "render_error", logMessage]);
+      throw(["error", errType || "render_error", logMessage]);
     }
   };
 
@@ -347,6 +366,9 @@ var Render = (function() {
     },
     list : function(fun, ddoc, args) {
       runList(fun, ddoc, args);
+    },
+    rewrite : function(fun, ddoc, args) {
+      runRewrite(fun, ddoc, args);
     }
   };
 })();

http://git-wip-us.apache.org/repos/asf/couchdb/blob/101e5416/test/javascript/tests/rewrite_js.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/rewrite_js.js b/test/javascript/tests/rewrite_js.js
new file mode 100644
index 0000000..b2c165b
--- /dev/null
+++ b/test/javascript/tests/rewrite_js.js
@@ -0,0 +1,340 @@
+// 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.
+ 
+ 
+ 
+couchTests.rewrite = function(debug) {
+  if (debug) debugger;
+  var dbNames = ["test_suite_db", "test_suite_db/with_slashes"];
+  for (var i=0; i < dbNames.length; i++) {
+    var db = new CouchDB(dbNames[i]);
+    var dbName = encodeURIComponent(dbNames[i]);
+    db.deleteDb();
+    db.createDb();
+
+    var designDoc = {
+      _id:"_design/test",
+      language: "javascript",
+      _attachments:{
+        "foo.txt": {
+          content_type:"text/plain",
+          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        }
+      },
+      rewrites: stringFun(function(req) {
+        prefix = req.path[4];
+        if (prefix === 'foo') {
+            return 'foo.txt';
+        }
+        if (prefix === 'foo2') {
+            return {path: 'foo.txt', method: 'GET'};
+        }
+        if (prefix === 'hello') {
+            if (req.method != 'PUT') {
+                return
+            }
+            id = req.path[5];
+            return {path: '_update/hello/' + id};
+        }
+        if (prefix === 'welcome') {
+            if (req.path.length == 6){
+                name = req.path[5];
+                return {path: '_show/welcome', query: {'name': name}};
+            }
+            return '_show/welcome';
+        }
+        if (prefix === 'welcome2') {
+            return {path: '_show/welcome', query: {'name': 'user'}};
+        }
+        if (prefix === 'welcome3') {
+            name = req.path[5];
+            if (req.method == 'PUT') {
+                path = '_update/welcome2/' + name;
+            } else if (req.method == 'GET') {
+                path = '_show/welcome2/' + name;
+            } else {
+                return;
+            }
+            return path;
+        }
+        if (prefix === 'welcome4') {
+            return {path: '_show/welcome3',  query: {name: req.path[5]}};
+        }
+        if (prefix === 'welcome5') {
+            rest = req.path.slice(5).join('/');
+            return {path: '_show/' + rest,  query: {name: rest}};
+        }
+        if (prefix === 'basicView') {
+            rest = req.path.slice(5).join('/');
+            return {path: '_view/basicView'};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/basicView') {
+            return {path: '_list/simpleForm/basicView'};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/basicViewFixed') {
+            return {path: '_list/simpleForm/basicView',
+                    query: {startkey: '"3"', endkey: '"8"'}};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/complexView') {
+            return {path: '_list/simpleForm/complexView',
+                    query: {key: JSON.stringify([1,2])}};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/complexView2') {
+            return {path: '_list/simpleForm/complexView',
+                    query: {key: JSON.stringify(['test', {}])}};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/complexView3') {
+            return {path: '_list/simpleForm/complexView',
+                    query: {key: JSON.stringify(['test', ['test', 'essai']])}};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/complexView4') {
+            return {path: '_list/simpleForm/complexView2',
+                    query: {key: JSON.stringify({"c": 1})}};
+        }
+        if (req.path.slice(4).join('/') === 'simpleForm/complexView4') {
+            return {path: '_list/simpleForm/complexView2',
+                    query: {key: JSON.stringify({"c": 1})}};
+        }
+        if (req.path.slice(4).join('/') === '/') {
+            return {path: '_view/basicView'};
+        }
+        if (prefix === 'db') {
+            return {path: '../../' + req.path.slice(5).join('/')};
+        }
+      }),
+      lists: {
+        simpleForm: stringFun(function(head, req) {
+          log("simpleForm");
+          send('<ul>');
+          var row, row_number = 0, prevKey, firstKey = null;
+          while (row = getRow()) {
+            row_number += 1;
+            if (!firstKey) firstKey = row.key;
+            prevKey = row.key;
+            send('\n<li>Key: '+row.key
+                 +' Value: '+row.value
+                 +' LineNo: '+row_number+'</li>');
+          }
+          return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
+        }),
+      },
+      shows: {
+        "welcome": stringFun(function(doc,req) {
+          return "Welcome " + req.query["name"];
+        }),
+        "welcome2": stringFun(function(doc, req) {
+          return "Welcome " + doc.name;
+        }),
+        "welcome3": stringFun(function(doc,req) {
+          return "Welcome " + req.query["name"];
+        })
+      },
+      updates: {
+        "hello" : stringFun(function(doc, req) {
+          if (!doc) {
+            if (req.id) {
+              return [{
+                _id : req.id
+              }, "New World"]
+            }
+            return [null, "Empty World"];
+          }
+          doc.world = "hello";
+          doc.edited_by = req.userCtx;
+          return [doc, "hello doc"];
+        }),
+        "welcome2": stringFun(function(doc, req) {
+          if (!doc) {
+            if (req.id) {
+              return [{
+                _id: req.id,
+                name: req.id
+              }, "New World"]
+            }
+            return [null, "Empty World"];
+          }
+          return [doc, "hello doc"];
+        })
+      },
+      views : {
+        basicView : {
+          map : stringFun(function(doc) {
+            if (doc.integer) {
+              emit(doc.integer, doc.string);
+            }
+
+          })
+        },
+        complexView: {
+          map: stringFun(function(doc) {
+            if (doc.type == "complex") {
+              emit([doc.a, doc.b], doc.string);
+            }
+          })
+        },
+        complexView2: {
+          map: stringFun(function(doc) {
+            if (doc.type == "complex") {
+              emit(doc.a, doc.string);
+            }
+          })
+        },
+        complexView3: {
+          map: stringFun(function(doc) {
+            if (doc.type == "complex") {
+              emit(doc.b, doc.string);
+            }
+          })
+        }
+      }
+    }
+
+    db.save(designDoc);
+
+    var docs = makeDocs(0, 10);
+    db.bulkSave(docs);
+
+    var docs2 = [
+      {"a": 1, "b": 1, "string": "doc 1", "type": "complex"},
+      {"a": 1, "b": 2, "string": "doc 2", "type": "complex"},
+      {"a": "test", "b": {}, "string": "doc 3", "type": "complex"},
+      {"a": "test", "b": ["test", "essai"], "string": "doc 4", "type": "complex"},
+      {"a": {"c": 1}, "b": "", "string": "doc 5", "type": "complex"}
+    ];
+
+    db.bulkSave(docs2);
+
+    // test simple rewriting
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/foo");
+    T(req.responseText == "This is a base64 encoded text");
+    T(req.getResponseHeader("Content-Type") == "text/plain");
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/foo2");
+    T(req.responseText == "This is a base64 encoded text");
+    T(req.getResponseHeader("Content-Type") == "text/plain");
+
+
+    // test POST
+    // hello update world
+
+    var doc = {"word":"plankton", "name":"Rusty"}
+    var resp = db.save(doc);
+    T(resp.ok);
+    var docid = resp.id;
+
+    xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test/_rewrite/hello/"+docid);
+    T(xhr.status == 201);
+    T(xhr.responseText == "hello doc");
+    T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))
+
+    doc = db.open(docid);
+    T(doc.world == "hello");
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome?name=user");
+    T(req.responseText == "Welcome user");
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome/user");
+    T(req.responseText == "Welcome user");
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome2");
+    T(req.responseText == "Welcome user");
+
+    xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test/_rewrite/welcome3/test");
+    T(xhr.status == 201);
+    T(xhr.responseText == "New World");
+    T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));
+
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome3/test");
+    T(xhr.responseText == "Welcome test");
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome4/user");
+    T(req.responseText == "Welcome user");
+
+    req = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/welcome5/welcome3");
+    T(req.responseText == "Welcome welcome3");
+
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/basicView");
+    T(xhr.status == 200, "view call");
+    T(/{"total_rows":9/.test(xhr.responseText));
+
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView");
+    T(xhr.status == 200, "with query params");
+    T(/FirstKey: [1, 2]/.test(xhr.responseText));
+
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView2");
+    T(xhr.status == 200, "with query params");
+    T(/Value: doc 3/.test(xhr.responseText));
+
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView3");
+    T(xhr.status == 200, "with query params");
+    T(/Value: doc 4/.test(xhr.responseText));
+
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/simpleForm/complexView4");
+    T(xhr.status == 200, "with query params");
+    T(/Value: doc 5/.test(xhr.responseText));
+
+    // COUCHDB-2031 - path normalization versus qs params
+    xhr = CouchDB.request("GET", "/"+dbName+"/_design/test/_rewrite/db/_design/test?meta=true");
+    T(xhr.status == 200, "path normalization works with qs params");
+    var result = JSON.parse(xhr.responseText);
+    T(result['_id'] == "_design/test");
+    T(typeof(result['_revs_info']) === "object");
+
+    // test early response
+    var ddoc = {
+      _id: "_design/response",
+      rewrites: stringFun(function(req){
+        status = parseInt(req.query.status);
+        return {code: status,
+                body: JSON.stringify({"status": status}),
+                headers: {'x-foo': 'bar', 'Content-Type': 'application/json'}};
+      })
+    }
+    T(db.save(ddoc).ok);
+    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/response/_rewrite?status=200");
+    T(xhr.status == 200);
+    T(xhr.headers['x-foo'] == 'bar');
+    T(xhr.responseText == '{"status":200}');
+    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/response/_rewrite?status=451");
+    T(xhr.status == 451);
+    T(xhr.headers['Content-Type'] == 'application/json');
+    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/response/_rewrite?status=600");
+    T(xhr.status == 500);
+
+
+    // test path relative to server
+    var ddoc = {
+      _id: "_design/relative",
+      rewrites: stringFun(function(req){
+        return '../../../_uuids'
+      })
+    }
+    T(db.save(ddoc).ok);
+    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/relative/_rewrite/uuids");
+    T(xhr.status == 200);
+    var result = JSON.parse(xhr.responseText);
+    T(result.uuids.length == 1);
+
+    // test loop
+    var ddoc_loop = {
+      _id: "_design/loop",
+      rewrites: stringFun(function(req) {
+        return '_rewrite/loop';
+      })
+    };
+    db.save(ddoc_loop);
+    var url = "/"+dbName+"/_design/loop/_rewrite/loop";
+    var xhr = CouchDB.request("GET", url);
+    TEquals(400, xhr.status);
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/101e5416/test/view_server/query_server_spec.rb
----------------------------------------------------------------------
diff --git a/test/view_server/query_server_spec.rb b/test/view_server/query_server_spec.rb
index 77bc01d..59883c0 100644
--- a/test/view_server/query_server_spec.rb
+++ b/test/view_server/query_server_spec.rb
@@ -447,6 +447,30 @@ functions = {
         end.
     ERLANG
   },
+  "rewrite-basic" => {
+    "js" => <<-JS,
+    function(req) {
+      return "new/location";
+    }
+    JS
+    "erlang" => <<-ERLANG,
+        fun(Req) ->
+            {[{"path", "new/location"}]}
+        end.
+    ERLANG
+  },
+  "rewrite-no-rule" => {
+    "js" => <<-JS,
+    function(req) {
+      return;
+    }
+    JS
+    "erlang" => <<-ERLANG,
+        fun(Req) ->
+            undefined
+        end.
+    ERLANG
+  },
   "error" => {
     "js" => <<-JS,
     function() {
@@ -747,7 +771,44 @@ describe "query server normal case" do
           should == true
       end
     end
+
+  describe "ddoc rewrites" do
+    describe "simple rewrite" do
+      before(:all) do
+        @ddoc = {
+          "_id" => "foo",
+          "rewrites" => functions["rewrite-basic"][LANGUAGE]
+        }
+        @qs.teach_ddoc(@ddoc)
+      end
+      it "should run normal" do
+        ok, resp = @qs.ddoc_run(@ddoc,
+          ["rewrites"],
+          [{"path" => "foo/bar"}, {"method" => "POST"}]
+        )
+        ok.should == "ok"
+        resp["path"].should == "new/location"
+      end
+    end
+
+    describe "no rule" do
+      before(:all) do
+        @ddoc = {
+          "_id" => "foo",
+          "rewrites" => functions["rewrite-no-rule"][LANGUAGE]
+        }
+        @qs.teach_ddoc(@ddoc)
+      end
+      it "should run normal" do
+        resp = @qs.ddoc_run(@ddoc,
+          ["rewrites"],
+          [{"path" => "foo/bar"}, {"method" => "POST"}]
+        )
+        resp.should == ['no_dispatch_rule']
+      end
+    end
   end
+end
 
 
 


[2/2] couchdb commit: updated refs/heads/master to 37f4d55

Posted by kx...@apache.org.
Add make command to run query server tests


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/37f4d558
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/37f4d558
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/37f4d558

Branch: refs/heads/master
Commit: 37f4d55884fdd34888fa0488439766c4e1803c4c
Parents: 101e541
Author: Alexander Shorin <kx...@apache.org>
Authored: Wed Nov 18 22:43:57 2015 +0300
Committer: Alexander Shorin <kx...@apache.org>
Committed: Tue Dec 22 00:41:36 2015 +0300

----------------------------------------------------------------------
 Makefile | 6 ++++++
 1 file changed, 6 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/37f4d558/Makefile
----------------------------------------------------------------------
diff --git a/Makefile b/Makefile
index 82fb623..f692126 100644
--- a/Makefile
+++ b/Makefile
@@ -115,6 +115,12 @@ endif
 	@dev/run -n 1 -q --with-admin-party-please test/javascript/run $(suites)
 
 
+.PHONY: check-qs
+# target: check-qs - Run query server tests (ruby and rspec required!)
+check-qs:
+	@QS_LANG=js rspec test/view_server/query_server_spec.rb
+
+
 .PHONY: list-eunit-apps
 # target: list-eunit-apps - List EUnit target apps
 list-eunit-apps: