couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jch...@apache.org
Subject svn commit: r803245 - in /couchdb/trunk: etc/couchdb/ share/ share/server/ share/www/script/ src/couchdb/ test/
Date Tue, 11 Aug 2009 18:50:09 GMT
Author: jchris
Date: Tue Aug 11 18:50:08 2009
New Revision: 803245

URL: http://svn.apache.org/viewvc?rev=803245&view=rev
Log:
Initial commit of _update handler. Thanks to Paul Davis, Jason Davies for code and others
for discussion.

The _update handler accepts POSTs to paths like: /db/_design/foo/_update/bar and PUTs which
include docids, like: /db/_design/foo/_update/bar/docid

The function signature:

function(doc, req) {
  doc.a_new_field = req.query.something;
  return [doc, "<h1>added something to your doc</h1>"];
}

The tests in update_documents.js are fairly complete and include examples of bumping a counter,
changing only a single field, parsing from (and returning) XML, and creating new documents.


Modified:
    couchdb/trunk/etc/couchdb/default.ini.tpl.in
    couchdb/trunk/share/Makefile.am
    couchdb/trunk/share/server/loop.js
    couchdb/trunk/share/server/render.js
    couchdb/trunk/share/www/script/couch_tests.js
    couchdb/trunk/src/couchdb/couch_httpd.erl
    couchdb/trunk/src/couchdb/couch_httpd_external.erl
    couchdb/trunk/src/couchdb/couch_httpd_show.erl
    couchdb/trunk/src/couchdb/couch_query_servers.erl
    couchdb/trunk/test/query_server_spec.rb

Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
+++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Tue Aug 11 18:50:08 2009
@@ -90,3 +90,4 @@
 _show = {couch_httpd_show, handle_doc_show_req}
 _list = {couch_httpd_show, handle_view_list_req}
 _info = {couch_httpd_db,   handle_design_info_req}
+_update = {couch_httpd_show, handle_doc_update_req}

Modified: couchdb/trunk/share/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/share/Makefile.am (original)
+++ couchdb/trunk/share/Makefile.am Tue Aug 11 18:50:08 2009
@@ -137,6 +137,7 @@
     www/script/test/etags_head.js \
     www/script/test/etags_views.js \
     www/script/test/show_documents.js \
+    www/script/test/update_documents.js \
     www/script/test/list_views.js \
     www/script/test/compact.js \
     www/script/test/purge.js \

Modified: couchdb/trunk/share/server/loop.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/server/loop.js?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/share/server/loop.js (original)
+++ couchdb/trunk/share/server/loop.js Tue Aug 11 18:50:08 2009
@@ -41,6 +41,7 @@
   "rereduce" : Views.rereduce,
   "validate" : Validate.validate,
   "show"     : Render.show,
+  "update"   : Render.update,
   "list"     : Render.list,
   "filter"   : Filter.filter
 };

Modified: couchdb/trunk/share/server/render.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/server/render.js?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/share/server/render.js (original)
+++ couchdb/trunk/share/server/render.js Tue Aug 11 18:50:08 2009
@@ -166,11 +166,16 @@
 ////
 ////
 ////
+
 var Render = {
   show : function(funSrc, doc, req) {
     var showFun = compileFunction(funSrc);
     runShow(showFun, doc, req, funSrc);
   },
+  update : function(funSrc, doc, req) {
+    var upFun = compileFunction(funSrc);
+    runUpdate(upFun, doc, req, funSrc);
+  },
   list : function(head, req) {
     runList(funs[0], head, req, funsrc[0]);
   }
@@ -212,6 +217,21 @@
   }
 };
 
+function runUpdate(renderFun, doc, req, funSrc) {
+  try {
+    var result = renderFun.apply(null, [doc, req]);
+    var doc = result[0];
+    var resp = result[1];
+    if (resp) {
+      respond(["up", doc, maybeWrapResponse(resp)]);
+    } else {
+      renderError("undefined response from update function");
+    }
+  } catch(e) {
+    respondError(e, funSrc, true);
+  }
+};
+
 function resetList() {
   gotRow = false;
   lastRow = false;

Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Tue Aug 11 18:50:08 2009
@@ -67,6 +67,7 @@
 loadTest("etags_head.js");
 loadTest("etags_views.js");
 loadTest("show_documents.js");
+loadTest("update_documents.js");
 loadTest("list_views.js");
 loadTest("compact.js");
 loadTest("purge.js");

Modified: couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd.erl Tue Aug 11 18:50:08 2009
@@ -353,7 +353,7 @@
     {ok, MochiReq:respond({Code, Headers ++ server_header() ++ couch_httpd_auth:cookie_auth_header(Req,
Headers), Body})}.
 
 send_method_not_allowed(Req, Methods) ->
-    send_response(Req, 405, [{"Allow", Methods}], <<>>).
+    send_error(Req, 405, [{"Allow", Methods}], <<"method_not_allowed">>, ?l2b("Only
" ++ Methods ++ " allowed")).
 
 send_json(Req, Value) ->
     send_json(Req, 200, Value).

Modified: couchdb/trunk/src/couchdb/couch_httpd_external.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_external.erl?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_external.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_external.erl Tue Aug 11 18:50:08 2009
@@ -57,8 +57,7 @@
 json_req_obj(#httpd{mochi_req=Req,
                method=Verb,
                path_parts=Path,
-               req_body=ReqBody,
-               user_ctx=#user_ctx{name=UserName, roles=UserRoles}
+               req_body=ReqBody
             }, Db) ->
     Body = case ReqBody of
         undefined -> Req:recv_body();
@@ -70,7 +69,6 @@
         _ ->
             []
     end,
-    UserCtx = {[{<<"name">>, UserName}, {<<"roles">>, UserRoles}]},
     Headers = Req:get(headers),
     Hlist = mochiweb_headers:to_list(Headers),
     {ok, Info} = couch_db:get_db_info(Db),
@@ -83,7 +81,7 @@
         {<<"body">>, Body},
         {<<"form">>, to_json_terms(ParsedForm)},
         {<<"cookie">>, to_json_terms(Req:parse_cookie())},
-        {<<"userCtx">>, UserCtx}]}.
+        {<<"userCtx">>, couch_util:json_user_ctx(Db)}]}.
 
 to_json_terms(Data) ->
     to_json_terms(Data, []).

Modified: couchdb/trunk/src/couchdb/couch_httpd_show.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_show.erl?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_show.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_show.erl Tue Aug 11 18:50:08 2009
@@ -12,10 +12,9 @@
 
 -module(couch_httpd_show).
 
--export([handle_doc_show_req/2, handle_view_list_req/2,
+-export([handle_doc_show_req/2, handle_doc_update_req/2, handle_view_list_req/2,
         handle_doc_show/5, handle_view_list/6]).
 
-
 -include("couch_db.hrl").
 
 -import(couch_httpd,
@@ -40,6 +39,47 @@
 handle_doc_show_req(Req, _Db) ->
     send_method_not_allowed(Req, "GET,POST,HEAD").
 
+handle_doc_update_req(#httpd{
+        method = 'PUT',
+        path_parts=[_DbName, _Design, DesignName, _Update, UpdateName, 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">>),
+    UpdateSrc = couch_util:get_nested_json_value({Props}, [<<"updates">>, UpdateName]),
+    Doc = try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
+        FoundDoc -> FoundDoc
+    catch
+        _ -> nil
+    end,
+    send_doc_update_response(Lang, UpdateSrc, DocId, Doc, Req, Db);
+
+handle_doc_update_req(#httpd{
+        method = 'POST',
+        path_parts=[_DbName, _Design, DesignName, _Update, UpdateName]
+    }=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">>),
+    UpdateSrc = couch_util:get_nested_json_value({Props}, [<<"updates">>, UpdateName]),
+    send_doc_update_response(Lang, UpdateSrc, nil, nil, Req, Db);
+
+handle_doc_update_req(#httpd{
+        path_parts=[_DbName, _Design, DesignName, _Update, UpdateName, DocId]
+    }=Req, Db) ->
+    send_method_not_allowed(Req, "PUT");
+
+handle_doc_update_req(#httpd{
+        path_parts=[_DbName, _Design, DesignName, _Update, UpdateName]
+    }=Req, Db) ->
+    send_method_not_allowed(Req, "POST");
+
+handle_doc_update_req(Req, _Db) ->
+    send_error(Req, 404, <<"update_error">>, <<"Invalid path.">>).
+
+
+
+
 handle_doc_show(Req, DesignName, ShowName, DocId, Db) ->
     DesignId = <<"_design/", DesignName/binary>>,
     #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
@@ -364,18 +404,39 @@
         couch_httpd_external:send_external_response(Req, JsonResp)
     end).
 
-set_or_replace_header(H, L) ->
-    set_or_replace_header(H, L, []).
+send_doc_update_response(Lang, UpdateSrc, DocId, Doc, #httpd{mochi_req=MReq}=Req, Db) ->
+    case couch_query_servers:render_doc_update(Lang, UpdateSrc, 
+        DocId, Doc, Req, Db) of
+    [<<"up">>, {NewJsonDoc}, JsonResp] ->
+        Options = case couch_httpd:header_value(Req, "X-Couch-Full-Commit", "false") of
+        "true" ->
+            [full_commit];
+        _ ->
+            []
+        end,
+        NewDoc = couch_doc:from_json_obj({NewJsonDoc}),
+        Code = 201,
+        {ok, NewRev} = couch_db:update_doc(Db, NewDoc, Options);
+    [<<"up">>, _Other, JsonResp] ->
+        Code = 200,
+        ok
+    end,
+    JsonResp2 = json_apply_field({<<"code">>, Code}, JsonResp),
+    couch_httpd_external:send_external_response(Req, JsonResp2).
 
-set_or_replace_header({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
+% Maybe this is in the proplists API
+% todo move to couch_util
+json_apply_field(H, {L}) ->
+    json_apply_field(H, L, []).
+json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
     % drop matching keys
-    set_or_replace_header({Key, NewValue}, Headers, Acc);
-set_or_replace_header({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
+    json_apply_field({Key, NewValue}, Headers, Acc);
+json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
     % something else is next, leave it alone.
-    set_or_replace_header({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
-set_or_replace_header({Key, NewValue}, [], Acc) ->
+    json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
+json_apply_field({Key, NewValue}, [], Acc) ->
     % end of list, add ours
-    [{Key, NewValue}|Acc].
+    {[{Key, NewValue}|Acc]}.
 
 apply_etag({ExternalResponse}, CurrentEtag) ->
     % Here we embark on the delicate task of replacing or creating the
@@ -387,12 +448,12 @@
         % no JSON headers
         % add our Etag and Vary headers to the response
         {[{<<"headers">>, {[{<<"Etag">>, CurrentEtag}, {<<"Vary">>,
<<"Accept">>}]}} | ExternalResponse]};
-    {JsonHeaders} ->
+    JsonHeaders ->
         {[case Field of
-        {<<"headers">>, {JsonHeaders}} -> % add our headers
-            JsonHeadersEtagged = set_or_replace_header({<<"Etag">>, CurrentEtag},
JsonHeaders),
-            JsonHeadersVaried = set_or_replace_header({<<"Vary">>, <<"Accept">>},
JsonHeadersEtagged),
-            {<<"headers">>, {JsonHeadersVaried}};
+        {<<"headers">>, JsonHeaders} -> % add our headers
+            JsonHeadersEtagged = json_apply_field({<<"Etag">>, CurrentEtag},
JsonHeaders),
+            JsonHeadersVaried = json_apply_field({<<"Vary">>, <<"Accept">>},
JsonHeadersEtagged),
+            {<<"headers">>, JsonHeadersVaried};
         _ -> % skip non-header fields
             Field
         end || Field <- ExternalResponse]}

Modified: couchdb/trunk/src/couchdb/couch_query_servers.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_query_servers.erl?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_query_servers.erl (original)
+++ couchdb/trunk/src/couchdb/couch_query_servers.erl Tue Aug 11 18:50:08 2009
@@ -18,7 +18,7 @@
 -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/6, start_view_list/2,
+-export([render_doc_show/6, render_doc_update/6, start_view_list/2,
         render_list_head/4, render_list_row/4, render_list_tail/1]).
 -export([start_filter/2, filter_doc/4, end_filter/1]).
 % -export([test/0]).
@@ -170,6 +170,7 @@
     after
         ok = ret_os_process(Lang, Pid)
     end.
+% todo use json_apply_field
 append_docid(DocId, JsonReqIn) ->
     [{<<"docId">>, DocId} | JsonReqIn].
 
@@ -190,6 +191,23 @@
         ok = ret_os_process(Lang, Pid)
     end.
 
+render_doc_update(Lang, UpdateSrc, DocId, Doc, Req, Db) ->
+    Pid = get_os_process(Lang),
+    {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,
+    try couch_os_process:prompt(Pid, 
+        [<<"update">>, UpdateSrc, JsonDoc, JsonReq]) of
+    FormResp ->
+        FormResp
+    after
+        ok = ret_os_process(Lang, Pid)
+    end.
+
 start_view_list(Lang, ListSrc) ->
     Pid = get_os_process(Lang),
     true = couch_os_process:prompt(Pid, [<<"add_fun">>, ListSrc]),

Modified: couchdb/trunk/test/query_server_spec.rb
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/query_server_spec.rb?rev=803245&r1=803244&r2=803245&view=diff
==============================================================================
--- couchdb/trunk/test/query_server_spec.rb (original)
+++ couchdb/trunk/test/query_server_spec.rb Tue Aug 11 18:50:08 2009
@@ -242,12 +242,21 @@
   },
   "filter-basic" => {
     "js" => <<-JS
-      function(doc, req, userCtx) {
+      function(doc, req) {
         if (doc.good) {
           return true;
         }
       }
     JS
+  },
+  "update-basic" => {
+    "js" => <<-JS
+    function(doc, req) {
+      doc.world = "hello";
+      var resp = [doc, "hello doc"];
+      return resp;
+    }
+    JS
   }
 }
 
@@ -441,6 +450,19 @@
         should ==  [true, [true, false, true]]
     end
   end
+  
+  describe "update" do
+    before(:all) do
+      @fun = functions["update-basic"][LANGUAGE]
+      @qs.reset!
+    end
+    it "should return a doc and a resp body" do
+      up, doc, resp = @qs.run(["update", @fun, {"foo" => "gnarly"}, {"verb" => "POST"}])
+      up.should == "up"
+      doc.should == {"foo" => "gnarly", "world" => "hello"}
+      resp["body"].should == "hello doc"
+    end
+  end
 end
 
 def should_have_exited qs



Mime
View raw message