summaryrefslogtreecommitdiff
path: root/src/couchdb
diff options
context:
space:
mode:
Diffstat (limited to 'src/couchdb')
-rw-r--r--src/couchdb/couch_httpd.erl2
-rw-r--r--src/couchdb/couch_httpd_external.erl6
-rw-r--r--src/couchdb/couch_httpd_show.erl93
-rw-r--r--src/couchdb/couch_query_servers.erl20
4 files changed, 99 insertions, 22 deletions
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index fee5004e..fde02317 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -353,7 +353,7 @@ send_response(#httpd{mochi_req=MochiReq}=Req, Code, Headers, Body) ->
{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).
diff --git a/src/couchdb/couch_httpd_external.erl b/src/couchdb/couch_httpd_external.erl
index d6fa945f..0a222311 100644
--- a/src/couchdb/couch_httpd_external.erl
+++ b/src/couchdb/couch_httpd_external.erl
@@ -57,8 +57,7 @@ process_external_req(HttpReq, Db, Name) ->
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 @@ json_req_obj(#httpd{mochi_req=Req,
_ ->
[]
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 @@ json_req_obj(#httpd{mochi_req=Req,
{<<"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, []).
diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl
index 1428e612..01bf0055 100644
--- a/src/couchdb/couch_httpd_show.erl
+++ b/src/couchdb/couch_httpd_show.erl
@@ -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(#httpd{method='GET'}=Req, _Db) ->
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 @@ send_doc_show_response(Lang, ShowSrc, DocId, #doc{revs=Revs}=Doc, #httpd{mochi_r
couch_httpd_external:send_external_response(Req, JsonResp)
end).
-set_or_replace_header(H, L) ->
- set_or_replace_header(H, L, []).
-
-set_or_replace_header({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
+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).
+
+% 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 @@ apply_etag({ExternalResponse}, CurrentEtag) ->
% 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]}
diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl
index bb0cc853..fca7c85a 100644
--- a/src/couchdb/couch_query_servers.erl
+++ b/src/couchdb/couch_query_servers.erl
@@ -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 @@ validate_doc_update(Lang, FunSrc, EditDoc, DiskDoc, Ctx) ->
after
ok = ret_os_process(Lang, Pid)
end.
+% todo use json_apply_field
append_docid(DocId, JsonReqIn) ->
[{<<"docId">>, DocId} | JsonReqIn].
@@ -190,6 +191,23 @@ render_doc_show(Lang, ShowSrc, DocId, Doc, Req, Db) ->
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]),