couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jch...@apache.org
Subject svn commit: r792771 - in /couchdb/trunk: etc/couchdb/ share/www/script/test/ src/couchdb/
Date Fri, 10 Jul 2009 00:59:37 GMT
Author: jchris
Date: Fri Jul 10 00:59:36 2009
New Revision: 792771

URL: http://svn.apache.org/viewvc?rev=792771&view=rev
Log:
Apply patch from Benoit Chesneau's COUCHDB-404

Restores 0.8-style /db/_view view urls and adds an option to render views and documents as
other formats like:

/db/docid?show=blog/post
/db/_view/blog/posts?list=index

We're retaining the longer _design/appname paths as well because that resource is valuable
for reverse proxies and rewriters.


Modified:
    couchdb/trunk/etc/couchdb/default.ini.tpl.in
    couchdb/trunk/share/www/script/test/list_views.js
    couchdb/trunk/share/www/script/test/show_documents.js
    couchdb/trunk/src/couchdb/couch_db.hrl
    couchdb/trunk/src/couchdb/couch_httpd_db.erl
    couchdb/trunk/src/couchdb/couch_httpd_show.erl
    couchdb/trunk/src/couchdb/couch_httpd_view.erl

Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
+++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Fri Jul 10 00:59:36 2009
@@ -68,6 +68,7 @@
 _view_cleanup = {couch_httpd_db, handle_view_cleanup_req}
 _compact = {couch_httpd_db, handle_compact_req}
 _design = {couch_httpd_db, handle_design_req}
+_view = {couch_httpd_view, handle_db_view_req}
 _temp_view = {couch_httpd_view, handle_temp_view_req}
 _changes = {couch_httpd_db, handle_changes_req}
 

Modified: couchdb/trunk/share/www/script/test/list_views.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/list_views.js?rev=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/list_views.js (original)
+++ couchdb/trunk/share/www/script/test/list_views.js Fri Jul 10 00:59:36 2009
@@ -176,6 +176,10 @@
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
   T(xhr.status == 200, "standard get should be 200");
   T(/head0123456789tail/.test(xhr.responseText));
+  
+  var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=basicBasic");
+  T(xhr.status == 200, "standard get should be 200");
+  T(/head0123456789tail/.test(xhr.responseText));
 
   // test that etags are available
   var etag = xhr.getResponseHeader("etag");
@@ -183,6 +187,12 @@
     headers: {"if-none-match": etag}
   });
   T(xhr.status == 304);
+  
+  var etag = xhr.getResponseHeader("etag");
+  xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=basicBasic", {
+    headers: {"if-none-match": etag}
+  });
+  T(xhr.status == 304);
 
   // test the richness of the arguments
   xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView");
@@ -208,17 +218,28 @@
   T(resp.req.cookie);
 
   // get with query params
-  var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3");
+  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));
   T(/LastKey: 9/.test(xhr.responseText));
-
+  
+  var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=simpleForm&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));
+  T(/LastKey: 9/.test(xhr.responseText));
+  
   // with 0 rows
   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));
+  
+   var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=simpleForm&startkey=30");
+  T(xhr.status == 200, "0 rows");
+  T(/Total Rows/.test(xhr.responseText));
 
   //too many Get Rows
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/tooManyGetRows/basicView");
@@ -231,6 +252,12 @@
   T(xhr.status == 200, "reduce 0 rows");
   T(/Total Rows/.test(xhr.responseText));
   T(/LastKey: undefined/.test(xhr.responseText));
+  
+  // reduce with 0 rows
+   var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/withReduce?list=simpleForm&startkey=30");
+   T(xhr.status == 200, "reduce 0 rows");
+   T(/Total Rows/.test(xhr.responseText));
+   T(/LastKey: undefined/.test(xhr.responseText));
 
   // when there is a reduce present, but not used
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false");

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=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/show_documents.js (original)
+++ couchdb/trunk/share/www/script/test/show_documents.js Fri Jul 10 00:59:36 2009
@@ -147,6 +147,14 @@
 
   // Fix for COUCHDB-379
   T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB"));
+  
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/"+docid+"?show=template/hello");
+  T(xhr.responseText == "Hello World");
+  T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))
+
+  // Fix for COUCHDB-379
+  T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB"));
 
   // // error stacktraces
   // xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/render-error/"+docid);
@@ -163,7 +171,10 @@
   // show with doc
   xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid);
   T(xhr.responseText == "Just Rusty");
-
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/"+docid+"?show=template/just-name");
+  T(xhr.responseText == "Just Rusty");
+  
   // show with missing doc
   xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/missingdoc");
 

Modified: couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ couchdb/trunk/src/couchdb/couch_db.hrl Fri Jul 10 00:59:36 2009
@@ -167,7 +167,8 @@
     include_docs = false,
     stale = false,
     multi_get = false,
-    callback = nil
+    callback = nil,
+    list = nil
 }).
 
 -record(view_fold_helper_funs, {

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Fri Jul 10 00:59:36 2009
@@ -26,7 +26,8 @@
 -record(doc_query_args, {
     options = [],
     rev = nil,
-    open_revs = []
+    open_revs = [],
+    show = nil
 }).
 
 % Database request handlers
@@ -192,7 +193,6 @@
 handle_design_info_req(Req, _Db) ->
     send_method_not_allowed(Req, "GET").
 
-
 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
@@ -558,8 +558,6 @@
         end
     end).
 
-
-
 db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) ->
     % check for the existence of the doc to handle the 404 case.
     couch_doc_open(Db, DocId, nil, []),
@@ -572,43 +570,50 @@
 
 db_doc_req(#httpd{method='GET'}=Req, Db, DocId) ->
     #doc_query_args{
+        show = Format,
         rev = Rev,
         open_revs = Revs,
         options = Options
     } = parse_doc_query(Req),
-    case Revs of
-    [] ->
-        Doc = couch_doc_open(Db, DocId, Rev, Options),
-        DiskEtag = couch_httpd:doc_etag(Doc),
-        couch_httpd:etag_respond(Req, DiskEtag, fun() ->
-            Headers = case Doc#doc.meta of
-            [] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta
-            _ -> []
-            end,
-            send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
-        end);
-    _ ->
-        {ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options),
-        {ok, Resp} = start_json_response(Req, 200),
-        send_chunk(Resp, "["),
-        % We loop through the docs. The first time through the separator
-        % is whitespace, then a comma on subsequent iterations.
-        lists:foldl(
-            fun(Result, AccSeparator) ->
-                case Result of
-                {ok, Doc} ->
-                    JsonDoc = couch_doc:to_json_obj(Doc, Options),
-                    Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
-                    send_chunk(Resp, AccSeparator ++ Json);
-                {{not_found, missing}, RevId} ->
-                    Json = ?JSON_ENCODE({[{"missing", RevId}]}),
-                    send_chunk(Resp, AccSeparator ++ Json)
+    case Format of
+    nil ->
+        case Revs of
+        [] ->
+            Doc = couch_doc_open(Db, DocId, Rev, Options),
+            DiskEtag = couch_httpd:doc_etag(Doc),
+            couch_httpd:etag_respond(Req, DiskEtag, fun() ->
+                Headers = case Doc#doc.meta of
+                [] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta
+                _ -> []
                 end,
-                "," % AccSeparator now has a comma
-            end,
-            "", Results),
-        send_chunk(Resp, "]"),
-        end_json_response(Resp)
+                send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
+            end);
+        _ ->
+            {ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options),
+            {ok, Resp} = start_json_response(Req, 200),
+            send_chunk(Resp, "["),
+            % We loop through the docs. The first time through the separator
+            % is whitespace, then a comma on subsequent iterations.
+            lists:foldl(
+                fun(Result, AccSeparator) ->
+                    case Result of
+                    {ok, Doc} ->
+                        JsonDoc = couch_doc:to_json_obj(Doc, Options),
+                        Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
+                        send_chunk(Resp, AccSeparator ++ Json);
+                    {{not_found, missing}, RevId} ->
+                        Json = ?JSON_ENCODE({[{"missing", RevId}]}),
+                        send_chunk(Resp, AccSeparator ++ Json)
+                    end,
+                    "," % AccSeparator now has a comma
+                end,
+                "", Results),
+            send_chunk(Resp, "]"),
+            end_json_response(Resp)
+        end;
+    _ ->
+        {DesignName, ShowName} = Format,
+        couch_httpd_show:handle_doc_show(Req, DesignName, ShowName, DocId, Db)
     end;
 
 db_doc_req(#httpd{method='POST'}=Req, Db, DocId) ->
@@ -843,6 +848,16 @@
 db_attachment_req(Req, _Db, _DocId, _FileNameParts) ->
     send_method_not_allowed(Req, "DELETE,GET,HEAD,PUT").
 
+parse_doc_format(FormatStr) when is_binary(FormatStr) ->
+    parse_doc_format(?b2l(FormatStr));
+parse_doc_format(FormatStr) when is_list(FormatStr) ->
+    SplitFormat = lists:splitwith(fun($/) -> false; (_) -> true end, FormatStr),
+    case SplitFormat of
+        {DesignName, [$/ | ShowName]} -> {?l2b(DesignName), ?l2b(ShowName)};
+        _Else -> throw({bad_request, <<"Invalid doc format">>})
+    end;
+parse_doc_format(_BadFormatStr) ->
+    throw({bad_request, <<"Invalid doc format">>}).   
 
 parse_doc_query(Req) ->
     lists:foldl(fun({Key,Value}, Args) ->
@@ -875,6 +890,8 @@
         {"open_revs", RevsJsonStr} ->
             JsonArray = ?JSON_DECODE(RevsJsonStr),
             Args#doc_query_args{open_revs=[couch_doc:parse_rev(Rev) || Rev <- JsonArray]};
+        {"show", FormatStr} ->
+            Args#doc_query_args{show=parse_doc_format(FormatStr)};
         _Else -> % unknown key value pair, ignore.
             Args
         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=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_show.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_show.erl Fri Jul 10 00:59:36 2009
@@ -12,7 +12,8 @@
 
 -module(couch_httpd_show).
 
--export([handle_doc_show_req/2, handle_view_list_req/2]).
+-export([handle_doc_show_req/2, handle_view_list_req/2,
+        handle_doc_show/5, handle_view_list/6]).
 
 
 -include("couch_db.hrl").
@@ -26,58 +27,59 @@
         method='GET',
         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, nil, []),
-    Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
-    ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
-    Doc = try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
-        FoundDoc -> FoundDoc
-    catch
-        _ -> nil
-    end,
-    send_doc_show_response(Lang, ShowSrc, DocId, Doc, Req, Db);
-
+    handle_doc_show(Req, DesignName, ShowName, DocId, Db);
+        
 handle_doc_show_req(#httpd{
         method='GET',
         path_parts=[_DbName, _Design, DesignName, _Show, ShowName]
     }=Req, Db) ->
-    DesignId = <<"_design/", DesignName/binary>>,
-    #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
-    Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
-    ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
-    send_doc_show_response(Lang, ShowSrc, nil, nil, Req, Db);
+    handle_doc_show(Req, DesignName, ShowName, nil, Db);
 
 handle_doc_show_req(#httpd{method='GET'}=Req, _Db) ->
     send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>);
 
 handle_doc_show_req(Req, _Db) ->
     send_method_not_allowed(Req, "GET,HEAD").
-
-handle_view_list_req(#httpd{method='GET',
-        path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
+    
+handle_doc_show(Req, DesignName, ShowName, DocId, Db) ->
     DesignId = <<"_design/", DesignName/binary>>,
     #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
     Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
-    ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]),
-    send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, nil);
+    ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
+    Doc = case DocId of
+        nil -> nil;
+        _ ->
+        try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
+            FoundDoc -> FoundDoc
+        catch
+            _ -> nil
+        end
+    end,
+    send_doc_show_response(Lang, ShowSrc, DocId, Doc, Req, Db).
+    
+handle_view_list_req(#httpd{method='GET',
+        path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
+    handle_view_list(Req, DesignName, ListName, ViewName, Db, nil);
 
 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=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
-    DesignId = <<"_design/", DesignName/binary>>,
-    #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
-    Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
-    ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]),
     ReqBody = couch_httpd:body(Req),
     {Props2} = ?JSON_DECODE(ReqBody),
     Keys = proplists:get_value(<<"keys">>, Props2, nil),
-    send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req#httpd{req_body=ReqBody},
Db, Keys);
+    handle_view_list(Req#httpd{req_body=ReqBody}, DesignName, ListName, ViewName, Db, Keys);
 
 handle_view_list_req(Req, _Db) ->
     send_method_not_allowed(Req, "GET,POST,HEAD").
 
+handle_view_list(Req, DesignName, ListName, ViewName, Db, Keys) ->
+    DesignId = <<"_design/", DesignName/binary>>,
+    #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
+    Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
+    ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]),
+    send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys).
 
 get_nested_json_value({Props}, [Key|Keys]) ->
     case proplists:get_value(Key, Props, nil) of

Modified: couchdb/trunk/src/couchdb/couch_httpd_view.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_view.erl?rev=792771&r1=792770&r2=792771&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_view.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_view.erl Fri Jul 10 00:59:36 2009
@@ -13,7 +13,7 @@
 -module(couch_httpd_view).
 -include("couch_db.hrl").
 
--export([handle_view_req/2,handle_temp_view_req/2]).
+-export([handle_view_req/2,handle_temp_view_req/2,handle_db_view_req/2]).
 
 -export([get_stale_type/1, get_reduce_type/1, parse_view_params/3]).
 -export([make_view_fold_fun/6, finish_view_fold/3, view_row_obj/3]).
@@ -73,6 +73,49 @@
 handle_view_req(Req, _Db) ->
     send_method_not_allowed(Req, "GET,POST,HEAD").
 
+handle_db_view_req(#httpd{method='GET',
+        path_parts=[_Db, _View, DName, ViewName]}=Req, Db) ->
+    QueryArgs = couch_httpd_view:parse_view_params(Req, nil, nil),
+    #view_query_args{
+        list = ListName
+    } = QueryArgs,
+    ?LOG_DEBUG("ici ~p", [ListName]),
+    case ListName of
+        nil -> couch_httpd_view:design_doc_view(Req, Db, DName, ViewName, nil);
+        _ ->
+            couch_httpd_show:handle_view_list(Req, DName, ListName, ViewName, Db, nil)
+    end;    
+
+handle_db_view_req(#httpd{method='POST',
+        path_parts=[_Db, _View, DName, ViewName]}=Req, Db) ->
+    QueryArgs = couch_httpd_view:parse_view_params(Req, nil, nil),
+    #view_query_args{
+        list = ListName
+    } = QueryArgs,     
+    case ListName of 
+    nil ->     
+        {Fields} = couch_httpd:json_body_obj(Req),
+        case proplists:get_value(<<"keys">>, Fields, nil) of
+        nil ->
+            Fmt = "POST to view ~p/~p in database ~p with no keys member.",
+            ?LOG_DEBUG(Fmt, [DName, ViewName, Db]),
+            couch_httpd_view:design_doc_view(Req, Db, DName, ViewName, nil);
+        Keys when is_list(Keys) ->
+            couch_httpd_view:design_doc_view(Req, Db, DName, ViewName, Keys);
+        _ ->
+            throw({bad_request, "`keys` member must be a array."})
+        end;
+    _ ->
+        ReqBody = couch_httpd:body(Req),
+        {Props2} = ?JSON_DECODE(ReqBody),
+        Keys = proplists:get_value(<<"keys">>, Props2, nil),
+        couch_httpd_show:handle_view_list(Req#httpd{req_body=ReqBody}, 
+            DName, ListName, ViewName, Db, Keys)
+    end;
+
+handle_db_view_req(Req, _Db) ->
+    send_method_not_allowed(Req, "GET,POST,HEAD").
+
 handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
     couch_stats_collector:increment({httpd, temporary_view_reads}),
     {Props} = couch_httpd:json_body_obj(Req),
@@ -269,6 +312,8 @@
     [{reduce, parse_bool_param(Value)}];
 parse_view_param("include_docs", Value) ->
     [{include_docs, parse_bool_param(Value)}];
+parse_view_param("list", Value) ->
+    [{list, ?l2b(Value)}];
 parse_view_param("callback", _) ->
     []; % Verified in the JSON response functions
 parse_view_param(Key, Value) ->
@@ -298,6 +343,8 @@
     Args#view_query_args{end_docid=Value};
 validate_view_query(limit, Value, Args) ->
     Args#view_query_args{limit=Value};
+validate_view_query(list, Value, Args) ->
+    Args#view_query_args{list=Value};
 validate_view_query(stale, _, Args) ->
     Args;
 validate_view_query(descending, true, Args) ->



Mime
View raw message