Author: jan
Date: Wed Mar  4 00:15:07 2009
New Revision: 749852

URL: http://svn.apache.org/viewvc?rev=749852&view=rev
Log:
allow for handling 404s in document show functions

Modified:
    couchdb/trunk/share/www/script/test/show_documents.js
    couchdb/trunk/src/couchdb/couch_httpd_db.erl
    couchdb/trunk/src/couchdb/couch_httpd_show.erl
    couchdb/trunk/src/couchdb/couch_query_servers.erl

Modified: couchdb/trunk/share/www/script/test/show_documents.js
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/show_documents.js?rev=749852&r1=749851&r2=749852&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/show_documents.js (original)
+++ couchdb/trunk/share/www/script/test/show_documents.js Wed Mar  4 00:15:07 
2009
@@ -21,11 +21,15 @@
     _id:"_design/template",
     language: "javascript",
     shows: {
-      "hello" : stringFun(function(doc) { 
+      "hello" : stringFun(function(doc, req) { 
         if (doc) {
-          return "Hello World";             
+          return "Hello World";
         } else {
-          return "Empty World";
+          if(req.docId) {
+            return "New World";
+          } else {
+            return "Empty World";
+          }
         }
       }),
       "just-name" : stringFun(function(doc, req) {
@@ -122,161 +126,167 @@
   var xhr = CouchDB.request("GET", "/test_suite_db/_show/");
   T(xhr.status == 404);
   T(JSON.parse(xhr.responseText).reason == "Invalid path.");
-
+  
   // hello template world
   xhr = CouchDB.request("GET", "/test_suite_db/_show/template/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/_show/template/hello/");
   T(xhr.responseText == "Empty World");
 
+  // // hello template world (non-existing docid)
+  xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/hello/nonExistingDoc");
+  T(xhr.responseText == "New World");
   
   // show with doc
   xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/just-name/"+docid);
   T(xhr.responseText == "Just Rusty");
   
-  // show with missing doc
-  xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/just-name/missingdoc");
-  T(xhr.status == 404);
-  var resp = JSON.parse(xhr.responseText);
-  T(resp.error == "not_found");
-  T(resp.reason == "missing");
   
-  // show with missing func
-  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/missing/"+docid);
-  T(xhr.status == 404);
-  
-  // missing design doc
-  xhr = CouchDB.request("GET", 
"/test_suite_db/_show/missingdoc/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"
-    }
-  });
-  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.info.db_name, "test_suite_db"));
-  
-  // returning a content-type
-  xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/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;*/*"}
-  });
-  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;*/*"}
-  });
-  T(xhr.responseText.match(/PNG/))
-  T("image/png" == xhr.getResponseHeader("Content-Type"));
-  var etag2 = xhr.getResponseHeader("etag");
-  T(etag2 != etag);
-
-  // proper etags
-  // show with doc
-  xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/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}
-  });
-  // should be 304
-  T(xhr.status == 304);    
-
-  // update the doc
-  doc.name = "Crusty";
-  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}
-  });
-  // 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}
-  });
-  // should be 304
-  T(xhr.status == 304);
-
-  // 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}
-  });
-  // should be 304
-  T(xhr.status == 304);
-  
-  // update design doc function
-  designDoc.shows["just-name"] = (function(doc, req) {
-    return {
-      body : "Just old " + doc.name
-    };
-  }).toString();
-  T(db.save(designDoc).ok);
-
-  xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/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);
-  // 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'
-    }
-  });
-  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'
-    }
-  });
-  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"
-    }
-  });
-  T(xhr.getResponseHeader("Content-Type") == "application/x-foo");
-  T(xhr.responseText.match(/foofoo/));
+   // show with missing func
+   xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/missing/"+docid);
+   T(xhr.status == 404);
+   
+   // missing design doc
+   xhr = CouchDB.request("GET", 
"/test_suite_db/_show/missingdoc/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"
+     }
+   });
+   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.info.db_name, "test_suite_db"));
+   
+   // returning a content-type
+   xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/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;*/*"}
+   });
+   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;*/*"}
+   });
+   T(xhr.responseText.match(/PNG/))
+   T("image/png" == xhr.getResponseHeader("Content-Type"));
+   var etag2 = xhr.getResponseHeader("etag");
+   T(etag2 != etag);
+   
+   // proper etags
+   // show with doc
+   xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/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}
+   });
+   // should be 304
+   T(xhr.status == 304);    
+   
+   // update the doc
+   doc.name = "Crusty";
+   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}
+   });
+   // 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}
+   });
+   // should be 304
+   T(xhr.status == 304);
+   
+   // 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}
+   });
+   // should be 304
+   T(xhr.status == 304);
+   
+   // update design doc function
+   designDoc.shows["just-name"] = (function(doc, req) {
+     return {
+       body : "Just old " + doc.name
+     };
+   }).toString();
+   T(db.save(designDoc).ok);
+   
+   xhr = CouchDB.request("GET", 
"/test_suite_db/_show/template/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);
+   // 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'
+     }
+   });
+   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'
+     }
+   });
+   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"
+     }
+   });
+   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, {
+     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\".");
 };

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=749852&r1=749851&r2=749852&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Mar  4 00:15:07 2009
@@ -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, db_req/2, couch_doc_open/4, couch_doc_open/5]).
 
 -import(couch_httpd,
     [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
@@ -538,20 +538,27 @@
 %   couch_doc_open(Db, DocId, [], []).
 
 couch_doc_open(Db, DocId, Rev, Options) ->
+    couch_doc_open(Db, DocId, Rev, Options, true).
+
+couch_doc_open(Db, DocId, Rev, Options, Throw) ->
     case Rev of
     "" -> % open most recent rev
         case couch_db:open_doc(Db, DocId, Options) of
         {ok, Doc} ->
             Doc;
-         Error ->
-             throw(Error)
+         Error when Throw ->
+             throw(Error);
+         _ ->
+             nil
          end;
   _ -> % open a specific rev (deletions come back as stubs)
       case couch_db:open_doc_revs(Db, DocId, [Rev], Options) of
           {ok, [{ok, Doc}]} ->
               Doc;
-          {ok, [Else]} ->
-              throw(Else)
+          {ok, [Else]} when Throw ->
+              throw(Else);
+          {ok, _} ->
+              nil
       end
   end.
 

Modified: couchdb/trunk/src/couchdb/couch_httpd_show.erl
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_show.erl?rev=749852&r1=749851&r2=749852&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_show.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_show.erl Wed Mar  4 00:15:07 2009
@@ -24,14 +24,14 @@
     
 handle_doc_show_req(#httpd{
         method='GET',
-        path_parts=[_, _, DesignName, ShowName, Docid]
+        path_parts=[_, _, DesignName, ShowName, DocId]
     }=Req, Db) ->
     DesignId = <<"_design/", DesignName/binary>>,
     #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, [], []),
     Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
     ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
-    Doc = couch_httpd_db:couch_doc_open(Db, Docid, [], []),
-    send_doc_show_response(Lang, ShowSrc, Doc, Req, Db);
+    Doc = couch_httpd_db:couch_doc_open(Db, DocId, [], [], false),
+    send_doc_show_response(Lang, ShowSrc, DocId, Doc, Req, Db);
 
 handle_doc_show_req(#httpd{
         method='GET',
@@ -41,7 +41,7 @@
     #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, [], []),
     Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
     ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
-    send_doc_show_response(Lang, ShowSrc, nil, Req, Db);
+    send_doc_show_response(Lang, ShowSrc, nil, nil, Req, Db);
 
 handle_doc_show_req(#httpd{method='GET'}=Req, _Db) ->
     send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>);
@@ -257,7 +257,7 @@
         throw(Error)
     end.
 
-send_doc_show_response(Lang, ShowSrc, nil, #httpd{mochi_req=MReq}=Req, Db) ->
+send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, 
Db) ->
     % compute etag with no doc
     Headers = MReq:get(headers),
     Hlist = mochiweb_headers:to_list(Headers),
@@ -265,12 +265,12 @@
     CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, nil, Accept}),
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -> 
         ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, 
-            nil, Req, Db),
+            DocId, nil, Req, Db),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         couch_httpd_external:send_external_response(Req, JsonResp)
     end);
 
-send_doc_show_response(Lang, ShowSrc, #doc{revs=[DocRev|_]}=Doc, 
+send_doc_show_response(Lang, ShowSrc, DocId, #doc{revs=[DocRev|_]}=Doc, 
     #httpd{mochi_req=MReq}=Req, Db) ->
     % calculate the etag
     Headers = MReq:get(headers),
@@ -280,7 +280,7 @@
     % We know our etag now    
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -> 
         ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, 
-            Doc, Req, Db),
+            DocId, Doc, Req, Db),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         couch_httpd_external:send_external_response(Req, JsonResp)
     end).

Modified: couchdb/trunk/src/couchdb/couch_query_servers.erl
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_query_servers.erl?rev=749852&r1=749851&r2=749852&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_query_servers.erl (original)
+++ couchdb/trunk/src/couchdb/couch_query_servers.erl Wed Mar  4 00:15:07 2009
@@ -18,7 +18,9 @@
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, 
handle_info/2,code_change/3,stop/0]).
 -export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
 -export([reduce/3, rereduce/3,validate_doc_update/5]).
--export([render_doc_show/5,start_view_list/2,render_list_head/5, 
render_list_row/4, render_list_tail/3, render_reduce_head/3, 
render_reduce_row/4]).
+-export([render_doc_show/6,start_view_list/2,render_list_head/5, 
+        render_list_row/4, render_list_tail/3, render_reduce_head/3, 
+        render_reduce_row/4]).
 % -export([test/0]).
 
 -include("couch_db.hrl").
@@ -122,14 +124,18 @@
     after
         ok = ret_os_process(Lang, Pid)
     end.
+append_docid(DocId, JsonReqIn) ->
+    [{<<"docId">>, DocId} | JsonReqIn].
 
-render_doc_show(Lang, ShowSrc, Doc, Req, Db) ->
+render_doc_show(Lang, ShowSrc, DocId, Doc, Req, Db) ->
     Pid = get_os_process(Lang),
-    JsonDoc = case Doc of
-        nil -> null;
-        _ -> couch_doc:to_json_obj(Doc, [revs])
+    {JsonReqIn} = couch_httpd_external:json_req_obj(Req, Db),
+
+    {JsonReq, JsonDoc} = case {DocId, Doc} of
+        {nil, nil} -> {{JsonReqIn}, null};
+        {DocId, nil} -> {{append_docid(DocId, JsonReqIn)}, null};
+        _ -> {{append_docid(DocId, JsonReqIn)}, couch_doc:to_json_obj(Doc, 
[revs])}
     end,
-    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
     try couch_os_process:prompt(Pid, 
         [<<"show_doc">>, ShowSrc, JsonDoc, JsonReq]) of
     FormResp ->


Reply via email to