summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Christopher Anderson <jchris@apache.org>2009-03-09 19:52:54 +0000
committerJohn Christopher Anderson <jchris@apache.org>2009-03-09 19:52:54 +0000
commit610064aea6eb707fe3b6a68f7c93005ceac0a2ec (patch)
tree432a31e5c0561e10f9b1e4973c2741c5c777c1f1
parentdd79e85bc9e4849df904498ca25ec56304440b5f (diff)
merge design doc resource branch. breaking changes to _view query paths.
closes COUCHDB-280 git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@751813 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES7
-rw-r--r--etc/couchdb/default.ini.tpl.in9
-rw-r--r--share/www/script/couch.js9
-rw-r--r--share/www/script/jquery.couch.js7
-rw-r--r--share/www/script/test/etags_views.js8
-rw-r--r--share/www/script/test/list_views.js67
-rw-r--r--share/www/script/test/show_documents.js115
-rw-r--r--src/couchdb/couch_db.hrl3
-rw-r--r--src/couchdb/couch_httpd.erl16
-rw-r--r--src/couchdb/couch_httpd_db.erl12
-rw-r--r--src/couchdb/couch_httpd_show.erl10
-rw-r--r--src/couchdb/couch_httpd_view.erl10
12 files changed, 155 insertions, 118 deletions
diff --git a/CHANGES b/CHANGES
index 396f82f6..5419e55d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,13 @@ Futon Utility Client:
* Added pagination to the database listing page.
* Implemented attachment uploading from the document page.
+Design Document Resource Paths:
+
+ * Added httpd_design_handlers config section.
+ * Moved _view to httpd_design_handlers.
+ * Added ability to render documents as non-JSON content-types with _show and
+ _list functions, which are also httpd_design_handlers.
+
Version 0.8.1-incubating
------------------------
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index 8deca3cf..dd63db0b 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -52,13 +52,16 @@ _restart = {couch_httpd_misc_handlers, handle_restart_req}
_stats = {couch_httpd_stats_handlers, handle_stats_req}
[httpd_db_handlers]
-_view = {couch_httpd_view, handle_view_req}
+_design = {couch_httpd_db, handle_design_req}
_temp_view = {couch_httpd_view, handle_temp_view_req}
-_show = {couch_httpd_show, handle_doc_show_req}
-_list = {couch_httpd_show, handle_view_list_req}
; The external module takes an optional argument allowing you to narrow it to a
; single script. Otherwise the script name is inferred from the first path section
; after _external's own path.
; _mypath = {couch_httpd_external, handle_external_req, <<"mykey">>}
; _external = {couch_httpd_external, handle_external_req}
+
+[httpd_design_handlers]
+_view = {couch_httpd_view, handle_view_req}
+_show = {couch_httpd_show, handle_doc_show_req}
+_list = {couch_httpd_show, handle_view_list_req}
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index 8a0d3d23..821fb694 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -142,12 +142,13 @@ function CouchDB(name, httpHeaders) {
}
this.view = function(viewname, options, keys) {
+ var viewParts = viewname.split('/');
+ var viewPath = this.uri + "_design/" + viewParts[0] + "/_view/"
+ + viewParts[1] + encodeOptions(options);
if(!keys) {
- this.last_req = this.request("GET", this.uri + "_view/" +
- viewname + encodeOptions(options));
+ this.last_req = this.request("GET", viewPath);
} else {
- this.last_req = this.request("POST", this.uri + "_view/" +
- viewname + encodeOptions(options), {
+ this.last_req = this.request("POST", viewPath, {
headers: {"Content-Type": "application/json"},
body: JSON.stringify({keys:keys})
});
diff --git a/share/www/script/jquery.couch.js b/share/www/script/jquery.couch.js
index 0c55aa49..ace9a8a6 100644
--- a/share/www/script/jquery.couch.js
+++ b/share/www/script/jquery.couch.js
@@ -191,9 +191,9 @@
appName = appName.join('/');
index = ddoc.couchapp && ddoc.couchapp.index;
if (index) {
- appPath = ['', name, index[0], appName, index[1]].join('/');
+ appPath = ['', name, ddoc._id, index].join('/');
} else if (ddoc._attachments && ddoc._attachments["index.html"]) {
- appPath = ['', name, '_design', appName, "index.html"].join('/');
+ appPath = ['', name, ddoc._id, "index.html"].join('/');
}
if (appPath) options.eachApp(appName, appPath, ddoc);
}
@@ -298,8 +298,9 @@
},
view: function(name, options) {
options = options || {};
+ name = name.split('/');
$.ajax({
- type: "GET", url: this.uri + "_view/" + name + encodeOptions(options),
+ type: "GET", url: this.uri + "_design/" + name[0] + "/_view/" + name[1] + encodeOptions(options),
dataType: "json",
complete: function(req) {
var resp = $.httpData(req, "json");
diff --git a/share/www/script/test/etags_views.js b/share/www/script/test/etags_views.js
index 808a6829..cb38ccf6 100644
--- a/share/www/script/test/etags_views.js
+++ b/share/www/script/test/etags_views.js
@@ -46,20 +46,20 @@ couchTests.etags_views = function(debug) {
T(saveResult.ok);
// verify get w/Etag on map view
- xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
T(xhr.status == 200);
var etag = xhr.getResponseHeader("etag");
- xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView", {
headers: {"if-none-match": etag}
});
T(xhr.status == 304);
// TODO GET with keys (when that is available)
// reduce view
- xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/withReduce");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
T(xhr.status == 200);
var etag = xhr.getResponseHeader("etag");
- xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/withReduce", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce", {
headers: {"if-none-match": etag}
});
T(xhr.status == 304);
diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js
index 3e5e8db4..055ef51c 100644
--- a/share/www/script/test/list_views.js
+++ b/share/www/script/test/list_views.js
@@ -144,8 +144,8 @@ couchTests.list_views = function(debug) {
T(view.total_rows == 10);
// standard get
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView");
- T(xhr.status == 200);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView");
+ T(xhr.status == 200, "standard get should be 200");
T(/Total Rows/.test(xhr.responseText));
T(/Key: 1/.test(xhr.responseText));
T(/LineNo: 0/.test(xhr.responseText));
@@ -159,14 +159,14 @@ couchTests.list_views = function(debug) {
// test that etags are available
var etag = xhr.getResponseHeader("etag");
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView", {
headers: {"if-none-match": etag}
});
T(xhr.status == 304);
// get with query params
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView?startkey=3");
- T(xhr.status == 200);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3");
+ T(xhr.status == 200, "with query params");
T(/Total Rows/.test(xhr.responseText));
T(!(/Key: 1/.test(xhr.responseText)));
T(/FirstKey: 3/.test(xhr.responseText));
@@ -174,33 +174,33 @@ couchTests.list_views = function(debug) {
// with 0 rows
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView?startkey=30");
- T(xhr.status == 200);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30");
+ T(xhr.status == 200, "0 rows");
T(/Total Rows/.test(xhr.responseText));
T(/Offset: null/.test(xhr.responseText));
// reduce with 0 rows
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?startkey=30");
- T(xhr.status == 200);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?startkey=30");
+ T(xhr.status == 200, "reduce 0 rows");
T(/Total Rows/.test(xhr.responseText));
T(/Offset: undefined/.test(xhr.responseText));
// when there is a reduce present, but not used
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?reduce=false");
- T(xhr.status == 200);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false");
+ T(xhr.status == 200, "reduce false");
T(/Total Rows/.test(xhr.responseText));
T(/Key: 1/.test(xhr.responseText));
// when there is a reduce present, and used
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true");
- T(xhr.status == 200);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true");
+ T(xhr.status == 200, "group reduce");
T(/Key: 1/.test(xhr.responseText));
// there should be etags on reduce as well
var etag = xhr.getResponseHeader("etag");
T(etag, "Etags should be served with reduce lists");
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
headers: {"if-none-match": etag}
});
T(xhr.status == 304);
@@ -210,13 +210,13 @@ couchTests.list_views = function(debug) {
var saveResult = db.bulkSave(docs);
T(saveResult.ok);
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
headers: {"if-none-match": etag}
});
- T(xhr.status == 200);
+ T(xhr.status == 200, "reduce etag");
// with accept headers for HTML
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/acceptSwitch/basicView", {
headers: {
"Accept": 'text/html'
}
@@ -226,7 +226,7 @@ couchTests.list_views = function(debug) {
T(xhr.responseText.match(/Value/));
// now with xml
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/acceptSwitch/basicView", {
headers: {
"Accept": 'application/xml'
}
@@ -236,49 +236,40 @@ couchTests.list_views = function(debug) {
T(xhr.responseText.match(/entry/));
// now with extra qs params
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/qsParams/basicView?foo=blam");
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/qsParams/basicView?foo=blam");
T(xhr.responseText.match(/blam/));
// aborting iteration
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter/basicView");
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/basicView");
T(xhr.responseText.match(/^head 0 1 2 tail$/));
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter2/basicView");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView");
T(xhr.responseText.match(/^head 0 1 2 tail$/));
// aborting iteration with reduce
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter/withReduce?group=true");
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true");
T(xhr.responseText.match(/^head 0 1 2 tail$/));
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter2/withReduce?group=true");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true");
T(xhr.responseText.match(/^head 0 1 2 tail$/));
// empty list
- var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/emptyList/basicView");
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/basicView");
T(xhr.responseText.match(/^$/));
- xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/emptyList/withReduce?group=true");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/withReduce?group=true");
T(xhr.responseText.match(/^$/));
// multi-key fetch
- var xhr = CouchDB.request("POST", "/test_suite_db/_list/lists/simpleForm/basicView", {
+ var xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/basicView", {
body: '{"keys":[2,4,5,7]}'
});
- T(xhr.status == 200);
+ T(xhr.status == 200, "multi key");
T(/Total Rows/.test(xhr.responseText));
T(!(/Key: 1/.test(xhr.responseText)));
T(/Key: 2/.test(xhr.responseText));
T(/FirstKey: 2/.test(xhr.responseText));
T(/LastKey: 7/.test(xhr.responseText));
- xhr = CouchDB.request("POST", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true", {
- body: '{"keys":[2,4,5,7]}'
- });
- T(xhr.status == 200);
- T(/Total Rows/.test(xhr.responseText));
- T(!(/Key: 1/.test(xhr.responseText)));
- T(/Key: 2/.test(xhr.responseText));
- T(/FirstKey: 2/.test(xhr.responseText));
- T(/LastKey: 7/.test(xhr.responseText));
-
+
// no multi-key fetch allowed when group=false
- xhr = CouchDB.request("POST", "/test_suite_db/_list/lists/simpleForm/withReduce?group=false", {
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=false", {
body: '{"keys":[2,4,5,7]}'
});
T(xhr.status == 400);
diff --git a/share/www/script/test/show_documents.js b/share/www/script/test/show_documents.js
index b9a66a1d..ed1ab3b3 100644
--- a/share/www/script/test/show_documents.js
+++ b/share/www/script/test/show_documents.js
@@ -33,9 +33,16 @@ couchTests.show_documents = function(debug) {
}
}),
"just-name" : stringFun(function(doc, req) {
- return {
- body : "Just " + doc.name
- };
+ if (doc) {
+ return {
+ body : "Just " + doc.name
+ };
+ } else {
+ return {
+ body : "No such doc",
+ code : 404
+ };
+ }
}),
"req-info" : stringFun(function(doc, req) {
return {
@@ -123,68 +130,72 @@ couchTests.show_documents = function(debug) {
var docid = resp.id;
// show error
- var xhr = CouchDB.request("GET", "/test_suite_db/_show/");
- T(xhr.status == 404);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/");
+ T(xhr.status == 404, 'Should be missing');
T(JSON.parse(xhr.responseText).reason == "Invalid path.");
// hello template world
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/"+docid);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/"+docid);
T(xhr.responseText == "Hello World");
// hello template world (no docid)
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello");
T(xhr.responseText == "Empty World");
// // hello template world (non-existing docid)
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/nonExistingDoc");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/nonExistingDoc");
T(xhr.responseText == "New World");
// show with doc
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid);
T(xhr.responseText == "Just Rusty");
+ // show with missing doc
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/missingdoc");
+ console.log(xhr)
+ T(xhr.status == 404, 'Doc should be missing');
+ T(xhr.responseText == "No such doc");
// show with missing func
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/missing/"+docid);
- T(xhr.status == 404);
-
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/missing/"+docid);
+ T(xhr.status == 404, "function is missing");
+
// missing design doc
- xhr = CouchDB.request("GET", "/test_suite_db/_show/missingdoc/just-name/"+docid);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/missingddoc/_show/just-name/"+docid);
T(xhr.status == 404);
var resp = JSON.parse(xhr.responseText);
T(resp.error == "not_found");
// query parameters
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/req-info/"+docid+"?foo=bar", {
- headers: {
- "Accept": "text/html;text/plain;*/*",
- "X-Foo" : "bar"
- }
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/req-info/"+docid+"?foo=bar", {
+ headers: {
+ "Accept": "text/html;text/plain;*/*",
+ "X-Foo" : "bar"
+ }
});
var resp = JSON.parse(xhr.responseText);
T(equals(resp.headers["X-Foo"], "bar"));
T(equals(resp.query, {foo:"bar"}));
T(equals(resp.verb, "GET"));
- T(equals(resp.path[4], docid));
+ T(equals(resp.path[5], docid));
T(equals(resp.info.db_name, "test_suite_db"));
// returning a content-type
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/xml-type/"+docid);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/xml-type/"+docid);
T("application/xml" == xhr.getResponseHeader("Content-Type"));
T("Accept" == xhr.getResponseHeader("Vary"));
// accept header switching
- // different mime has different etag
-
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, {
- headers: {"Accept": "text/html;text/plain;*/*"}
+ // different mime has different etag
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/accept-switch/"+docid, {
+ headers: {"Accept": "text/html;text/plain;*/*"}
});
T("text/html" == xhr.getResponseHeader("Content-Type"));
T("Accept" == xhr.getResponseHeader("Vary"));
var etag = xhr.getResponseHeader("etag");
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, {
- headers: {"Accept": "image/png;*/*"}
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/accept-switch/"+docid, {
+ headers: {"Accept": "image/png;*/*"}
});
T(xhr.responseText.match(/PNG/))
T("image/png" == xhr.getResponseHeader("Content-Type"));
@@ -193,12 +204,12 @@ couchTests.show_documents = function(debug) {
// proper etags
// show with doc
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid);
// extract the ETag header values
etag = xhr.getResponseHeader("etag");
// get again with etag in request
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
- headers: {"if-none-match": etag}
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
+ headers: {"if-none-match": etag}
});
// should be 304
T(xhr.status == 304);
@@ -208,16 +219,16 @@ couchTests.show_documents = function(debug) {
resp = db.save(doc);
T(resp.ok);
// req with same etag
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
- headers: {"if-none-match": etag}
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
+ headers: {"if-none-match": etag}
});
// status is 200
T(xhr.status == 200);
// get new etag and request again
etag = xhr.getResponseHeader("etag");
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
- headers: {"if-none-match": etag}
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
+ headers: {"if-none-match": etag}
});
// should be 304
T(xhr.status == 304);
@@ -225,9 +236,9 @@ couchTests.show_documents = function(debug) {
// update design doc (but not function)
designDoc.isChanged = true;
T(db.save(designDoc).ok);
-
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
- headers: {"if-none-match": etag}
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
+ headers: {"if-none-match": etag}
});
// should be 304
T(xhr.status == 304);
@@ -240,49 +251,49 @@ couchTests.show_documents = function(debug) {
}).toString();
T(db.save(designDoc).ok);
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
- headers: {"if-none-match": etag}
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid, {
+ headers: {"if-none-match": etag}
});
// status is 200
T(xhr.status == 200);
// JS can't set etag
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/no-set-etag/"+docid);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/no-set-etag/"+docid);
// extract the ETag header values
etag = xhr.getResponseHeader("etag");
T(etag != "skipped")
// test the respondWith mime matcher
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
- headers: {
- "Accept": 'text/html,application/atom+xml; q=0.9'
- }
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/respondWith/"+docid, {
+ headers: {
+ "Accept": 'text/html,application/atom+xml; q=0.9'
+ }
});
T(xhr.getResponseHeader("Content-Type") == "text/html");
T(xhr.responseText == "Ha ha, you said \"plankton\".");
// now with xml
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
- headers: {
- "Accept": 'application/xml'
- }
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/respondWith/"+docid, {
+ headers: {
+ "Accept": 'application/xml'
+ }
});
T(xhr.getResponseHeader("Content-Type") == "application/xml");
T(xhr.responseText.match(/node/));
T(xhr.responseText.match(/plankton/));
// registering types works
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
- headers: {
- "Accept": "application/x-foo"
- }
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/respondWith/"+docid, {
+ headers: {
+ "Accept": "application/x-foo"
+ }
});
T(xhr.getResponseHeader("Content-Type") == "application/x-foo");
T(xhr.responseText.match(/foofoo/));
// test the respondWith mime matcher without
- xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/respondWith/"+docid, {
headers: {
"Accept": 'text/html,application/atom+xml; q=0.9'
}
diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
index aa97a19c..026afe14 100644
--- a/src/couchdb/couch_db.hrl
+++ b/src/couchdb/couch_db.hrl
@@ -61,7 +61,8 @@
path_parts,
db_url_handlers,
user_ctx,
- req_body = undefined
+ req_body = undefined,
+ design_url_handlers
}).
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index 44ae0bb1..48ff403b 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -13,7 +13,7 @@
-module(couch_httpd).
-include("couch_db.hrl").
--export([start_link/0, stop/0, handle_request/3]).
+-export([start_link/0, stop/0, handle_request/4]).
-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1,absolute_uri/2]).
-export([verify_is_server_admin/1,unquote/1,quote/1,recv/2,recv_chunked/4]).
@@ -44,11 +44,18 @@ start_link() ->
fun({UrlKey, SpecStr}) ->
{?l2b(UrlKey), make_arity_2_fun(SpecStr)}
end, couch_config:get("httpd_db_handlers")),
+
+ DesignUrlHandlersList = lists:map(
+ fun({UrlKey, SpecStr}) ->
+ {?l2b(UrlKey), make_arity_2_fun(SpecStr)}
+ end, couch_config:get("httpd_design_handlers")),
+
UrlHandlers = dict:from_list(UrlHandlersList),
DbUrlHandlers = dict:from_list(DbUrlHandlersList),
+ DesignUrlHandlers = dict:from_list(DesignUrlHandlersList),
Loop = fun(Req)->
apply(?MODULE, handle_request,
- [Req, UrlHandlers, DbUrlHandlers])
+ [Req, UrlHandlers, DbUrlHandlers, DesignUrlHandlers])
end,
% and off we go
@@ -101,7 +108,7 @@ stop() ->
mochiweb_http:stop(?MODULE).
-handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
+handle_request(MochiReq, UrlHandlers, DbUrlHandlers, DesignUrlHandlers) ->
statistics(runtime), % prepare request_time counter, see end of function
AuthenticationFun = make_arity_1_fun(
couch_config:get("httpd", "authentication_handler")),
@@ -147,7 +154,8 @@ handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
method = Method,
path_parts = [list_to_binary(couch_httpd:unquote(Part))
|| Part <- string:tokens(Path, "/")],
- db_url_handlers = DbUrlHandlers
+ db_url_handlers = DbUrlHandlers,
+ design_url_handlers = DesignUrlHandlers
},
DefaultFun = fun couch_httpd_db:handle_request/1,
HandlerFun = couch_util:dict_find(HandlerKey, UrlHandlers, DefaultFun),
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 005e32d1..75022cd3 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -13,7 +13,7 @@
-module(couch_httpd_db).
-include("couch_db.hrl").
--export([handle_request/1, db_req/2, couch_doc_open/4]).
+-export([handle_request/1, handle_design_req/2, db_req/2, couch_doc_open/4]).
-import(couch_httpd,
[send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
@@ -41,6 +41,16 @@ handle_request(#httpd{path_parts=[DbName|RestParts],method=Method,
do_db_req(Req, Handler)
end.
+handle_design_req(#httpd{
+ path_parts=[_DbName,_Design,_DesName, <<"_",_/binary>> = Action | _Rest],
+ design_url_handlers = DesignUrlHandlers
+ }=Req, Db) ->
+ Handler = couch_util:dict_find(Action, DesignUrlHandlers, fun db_req/2),
+ Handler(Req, Db);
+
+handle_design_req(Req, Db) ->
+ db_req(Req, Db).
+
create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
ok = couch_httpd:verify_is_server_admin(Req),
case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl
index 5a03d9de..7b6f2832 100644
--- a/src/couchdb/couch_httpd_show.erl
+++ b/src/couchdb/couch_httpd_show.erl
@@ -24,7 +24,7 @@
handle_doc_show_req(#httpd{
method='GET',
- path_parts=[_, _, DesignName, ShowName, DocId]
+ path_parts=[_DbName, _Design, DesignName, _Show, ShowName, DocId]
}=Req, Db) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, [], []),
@@ -39,7 +39,7 @@ handle_doc_show_req(#httpd{
handle_doc_show_req(#httpd{
method='GET',
- path_parts=[_, _, DesignName, ShowName]
+ path_parts=[_DbName, _Design, DesignName, _Show, ShowName]
}=Req, Db) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, [], []),
@@ -53,7 +53,8 @@ handle_doc_show_req(#httpd{method='GET'}=Req, _Db) ->
handle_doc_show_req(Req, _Db) ->
send_method_not_allowed(Req, "GET,HEAD").
-handle_view_list_req(#httpd{method='GET',path_parts=[_, _, DesignName, ListName, ViewName]}=Req, Db) ->
+handle_view_list_req(#httpd{method='GET',
+ path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, [], []),
Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
@@ -63,7 +64,8 @@ handle_view_list_req(#httpd{method='GET',path_parts=[_, _, DesignName, ListName,
handle_view_list_req(#httpd{method='GET'}=Req, _Db) ->
send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>);
-handle_view_list_req(#httpd{method='POST',path_parts=[_, _, DesignName, ListName, ViewName]}=Req, Db) ->
+handle_view_list_req(#httpd{method='POST',
+ path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, [], []),
Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 6b9befe1..322cf945 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -49,13 +49,15 @@ design_doc_view(Req, Db, Id, ViewName, Keys) ->
couch_stats_collector:increment({httpd, view_reads}),
Result.
-handle_view_req(#httpd{method='GET',path_parts=[_,_, Id, ViewName]}=Req, Db) ->
- design_doc_view(Req, Db, Id, ViewName, nil);
+handle_view_req(#httpd{method='GET',
+ path_parts=[_Db, _Design, DName, _View, ViewName]}=Req, Db) ->
+ design_doc_view(Req, Db, DName, ViewName, nil);
-handle_view_req(#httpd{method='POST',path_parts=[_,_, Id, ViewName]}=Req, Db) ->
+handle_view_req(#httpd{method='POST',
+ path_parts=[_Db, _Design, DName, _View, ViewName]}=Req, Db) ->
{Props} = couch_httpd:json_body(Req),
Keys = proplists:get_value(<<"keys">>, Props, nil),
- design_doc_view(Req, Db, Id, ViewName, Keys);
+ design_doc_view(Req, Db, DName, ViewName, Keys);
handle_view_req(Req, _Db) ->
send_method_not_allowed(Req, "GET,POST,HEAD").