summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Kocoloski <adam@cloudant.com>2010-06-22 09:56:54 -0400
committerAdam Kocoloski <adam@cloudant.com>2010-08-12 11:18:46 -0400
commit4a4c4edd323db00d360db3355c5876b4f14a69a4 (patch)
tree569ef4f7e3a49488a1eec2e87ce23a553c1d2e6a
parent89ccaa276043a979ab38e7c90e581d2b220261ea (diff)
switch to 0.11-style (3-arity) design handlers, fix _show
_list and _update still need to be fixed, and _view reopens the ddoc
-rw-r--r--src/chttpd.erl11
-rw-r--r--src/chttpd_db.erl23
-rw-r--r--src/chttpd_show.erl129
-rw-r--r--src/chttpd_view.erl51
4 files changed, 109 insertions, 105 deletions
diff --git a/src/chttpd.erl b/src/chttpd.erl
index d178604d..a4bb1293 100644
--- a/src/chttpd.erl
+++ b/src/chttpd.erl
@@ -202,18 +202,17 @@ db_url_handlers() ->
{<<"_view_cleanup">>, fun chttpd_view:handle_view_cleanup_req/2},
{<<"_compact">>, fun chttpd_db:handle_compact_req/2},
{<<"_design">>, fun chttpd_db:handle_design_req/2},
- {<<"_view">>, fun chttpd_db:handle_db_view_req/2},
{<<"_temp_view">>, fun chttpd_db:handle_temp_view_req/2},
{<<"_changes">>, fun chttpd_db:handle_changes_req/2}
].
design_url_handlers() ->
[
- {<<"_view">>, fun chttpd_view:handle_view_req/2},
- {<<"_show">>, fun chttpd_show:handle_doc_show_req/2},
- {<<"_list">>, fun chttpd_show:handle_view_list_req/2},
- {<<"_update">>, fun chttpd_show:handle_doc_update_req/2},
- {<<"_info">>, fun chttpd_db:handle_design_info_req/2}
+ {<<"_view">>, fun chttpd_view:handle_view_req/3},
+ {<<"_show">>, fun chttpd_show:handle_doc_show_req/3},
+ {<<"_list">>, fun chttpd_show:handle_view_list_req/3},
+ {<<"_update">>, fun chttpd_show:handle_doc_update_req/3},
+ {<<"_info">>, fun chttpd_db:handle_design_info_req/3}
].
% Utilities
diff --git a/src/chttpd_db.erl b/src/chttpd_db.erl
index 2d2fadc2..0317aed4 100644
--- a/src/chttpd_db.erl
+++ b/src/chttpd_db.erl
@@ -16,7 +16,7 @@
-export([handle_request/1, handle_compact_req/2, handle_design_req/2,
db_req/2, couch_doc_open/4,handle_changes_req/2,
update_doc_result_to_json/1, update_doc_result_to_json/2,
- handle_design_info_req/2, handle_view_cleanup_req/2]).
+ handle_design_info_req/3, handle_view_cleanup_req/2]).
-import(chttpd,
[send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
@@ -123,23 +123,30 @@ handle_view_cleanup_req(Req, _) ->
chttpd:send_error(Req, 403, Msg).
handle_design_req(#httpd{
- path_parts=[_DbName,_Design,_DesName, <<"_",_/binary>> = Action | _Rest],
+ path_parts=[_DbName, _Design, Name, <<"_",_/binary>> = Action | _Rest],
design_url_handlers = DesignUrlHandlers
}=Req, Db) ->
- Handler = couch_util:get_value(Action, DesignUrlHandlers, fun db_req/2),
- Handler(Req, Db);
+ case fabric:open_doc(Db, <<"_design/", Name/binary>>, []) of
+ {ok, DDoc} ->
+ % TODO we'll trigger a badarity here if ddoc attachment starts with "_",
+ % or if user tries an unknown Action
+ Handler = couch_util:get_value(Action, DesignUrlHandlers, fun db_req/2),
+ Handler(Req, Db, DDoc);
+ Error ->
+ throw(Error)
+ end;
handle_design_req(Req, Db) ->
db_req(Req, Db).
-handle_design_info_req(#httpd{method='GET', path_parts=[_,_,Name,_]}=Req, Db) ->
- {ok, GroupInfoList} = fabric:get_view_group_info(Db, Name),
+handle_design_info_req(#httpd{method='GET'}=Req, Db, #doc{id=Id} = DDoc) ->
+ {ok, GroupInfoList} = fabric:get_view_group_info(Db, DDoc),
send_json(Req, 200, {[
- {name, <<"_design/", Name/binary>>},
+ {name, Id},
{view_index, {GroupInfoList}}
]});
-handle_design_info_req(Req, _Db) ->
+handle_design_info_req(Req, _Db, _DDoc) ->
send_method_not_allowed(Req, "GET").
create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
diff --git a/src/chttpd_show.erl b/src/chttpd_show.erl
index 9f4e0d02..f74b9dd6 100644
--- a/src/chttpd_show.erl
+++ b/src/chttpd_show.erl
@@ -12,38 +12,98 @@
-module(chttpd_show).
--export([handle_doc_show_req/2, handle_doc_update_req/2, handle_view_list_req/2,
- handle_doc_show/5, handle_view_list/7, start_list_resp/5,
- send_list_row/6]).
+-export([handle_doc_show_req/3, handle_doc_update_req/3, handle_view_list_req/3,
+ handle_view_list/7, get_fun_key/3]).
-include("chttpd.hrl").
-import(chttpd,
[send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
- start_json_response/2,send_chunk/2,send_chunked_error/2,
+ start_json_response/2,send_chunk/2,last_chunk/1,send_chunked_error/2,
start_chunked_response/3, send_error/4]).
+% /db/_design/foo/_show/bar/docid
+% show converts a json doc to a response of any content-type.
+% it looks up the doc an then passes it to the query server.
+% then it sends the response from the query server to the http client.
+
+maybe_open_doc(Db, DocId) ->
+ case fabric:open_doc(Db, DocId, [conflicts]) of
+ {ok, Doc} ->
+ Doc;
+ {not_found, _} ->
+ nil
+ end.
+
handle_doc_show_req(#httpd{
- method='GET',
- path_parts=[_DbName, _Design, DesignName, _Show, ShowName, DocId]
- }=Req, Db) ->
- handle_doc_show(Req, DesignName, ShowName, DocId, Db);
+ path_parts=[_, _, _, _, ShowName, DocId]
+ }=Req, Db, DDoc) ->
+
+ % open the doc
+ Doc = maybe_open_doc(Db, DocId),
+
+ % we don't handle revs here b/c they are an internal api
+ % returns 404 if there is no doc with DocId
+ handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId);
handle_doc_show_req(#httpd{
- path_parts=[_DbName, _Design, DesignName, _Show, ShowName]
- }=Req, Db) ->
- handle_doc_show(Req, DesignName, ShowName, nil, Db);
+ path_parts=[_, _, _, _, ShowName, DocId|Rest]
+ }=Req, Db, DDoc) ->
+
+ DocParts = [DocId|Rest],
+ DocId1 = ?l2b(string:join([?b2l(P)|| P <- DocParts], "/")),
-handle_doc_show_req(#httpd{method='GET'}=Req, _Db) ->
- send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>);
+ % open the doc
+ Doc = maybe_open_doc(Db, DocId1),
-handle_doc_show_req(Req, _Db) ->
- send_method_not_allowed(Req, "GET,POST,HEAD").
+ % we don't handle revs here b/c they are an internal api
+ % pass 404 docs to the show function
+ handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId1);
+
+handle_doc_show_req(#httpd{
+ path_parts=[_, _, _, _, ShowName]
+ }=Req, Db, DDoc) ->
+ % with no docid the doc is nil
+ handle_doc_show(Req, Db, DDoc, ShowName, nil);
+
+handle_doc_show_req(Req, _Db, _DDoc) ->
+ send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>).
+
+handle_doc_show(Req, Db, DDoc, ShowName, Doc) ->
+ handle_doc_show(Req, Db, DDoc, ShowName, Doc, null).
+
+handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId) ->
+ % get responder for ddoc/showname
+ CurrentEtag = show_etag(Req, Doc, DDoc, []),
+ chttpd:etag_respond(Req, CurrentEtag, fun() ->
+ JsonReq = chttpd_external:json_req_obj(Req, Db, DocId),
+ JsonDoc = couch_query_servers:json_doc(Doc),
+ [<<"resp">>, ExternalResp] =
+ couch_query_servers:ddoc_prompt(DDoc, [<<"shows">>, ShowName],
+ [JsonDoc, JsonReq]),
+ JsonResp = apply_etag(ExternalResp, CurrentEtag),
+ chttpd_external:send_external_response(Req, JsonResp)
+ end).
+
+
+show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) ->
+ Accept = chttpd:header_value(Req, "Accept"),
+ DocPart = case Doc of
+ nil -> nil;
+ Doc -> chttpd:doc_etag(Doc)
+ end,
+ couch_httpd:make_etag({couch_httpd:doc_etag(DDoc), DocPart, Accept,
+ UserCtx#user_ctx.roles, More}).
+
+get_fun_key(#doc{body={Props}}, Type, Name) ->
+ Lang = couch_util:get_value(<<"language">>, Props, <<"javascript">>),
+ Src = couch_util:get_nested_json_value({Props}, [Type, Name]),
+ {Lang, Src}.
handle_doc_update_req(#httpd{
method = 'PUT',
path_parts=[_DbName, _Design, DesignName, _Update, UpdateName, DocId]
- }=Req, Db) ->
+ }=Req, Db, _) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = chttpd_db:couch_doc_open(Db, DesignId, nil, []),
Lang = couch_util:get_value(<<"language">>, Props, <<"javascript">>),
@@ -58,7 +118,7 @@ handle_doc_update_req(#httpd{
handle_doc_update_req(#httpd{
method = 'POST',
path_parts=[_DbName, _Design, DesignName, _Update, UpdateName]
- }=Req, Db) ->
+ }=Req, Db, _) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = chttpd_db:couch_doc_open(Db, DesignId, nil, []),
Lang = couch_util:get_value(<<"language">>, Props, <<"javascript">>),
@@ -67,57 +127,38 @@ handle_doc_update_req(#httpd{
handle_doc_update_req(#httpd{
path_parts=[_DbName, _Design, _DesignName, _Update, _UpdateName, _DocId]
- }=Req, _Db) ->
+ }=Req, _Db, _) ->
send_method_not_allowed(Req, "PUT");
handle_doc_update_req(#httpd{
path_parts=[_DbName, _Design, _DesignName, _Update, _UpdateName]
- }=Req, _Db) ->
+ }=Req, _Db, _) ->
send_method_not_allowed(Req, "POST");
-handle_doc_update_req(Req, _Db) ->
+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}} = chttpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = couch_util:get_value(<<"language">>, Props, <<"javascript">>),
- ShowSrc = couch_util:get_nested_json_value({Props}, [<<"shows">>, ShowName]),
- Doc = case DocId of
- nil -> nil;
- _ ->
- try chttpd_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).
-
% view-list request with view and list from same design doc.
handle_view_list_req(#httpd{method='GET',
- path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
+ path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db, _) ->
handle_view_list(Req, DesignName, ListName, DesignName, ViewName, Db, nil);
% view-list request with view and list from different design docs.
handle_view_list_req(#httpd{method='GET',
- path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewDesignName, ViewName]}=Req, Db) ->
+ path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewDesignName, ViewName]}=Req, Db, _) ->
handle_view_list(Req, DesignName, ListName, ViewDesignName, ViewName, Db, nil);
-handle_view_list_req(#httpd{method='GET'}=Req, _Db) ->
+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) ->
+ path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db, _) ->
ReqBody = chttpd:body(Req),
{Props2} = ?JSON_DECODE(ReqBody),
Keys = couch_util:get_value(<<"keys">>, Props2, nil),
handle_view_list(Req#httpd{req_body=ReqBody}, DesignName, ListName, DesignName, ViewName, Db, Keys);
-handle_view_list_req(Req, _Db) ->
+handle_view_list_req(Req, _Db, _) ->
send_method_not_allowed(Req, "GET,POST,HEAD").
handle_view_list(Req, ListDesignName, ListName, ViewDesignName, ViewName, Db, Keys) ->
diff --git a/src/chttpd_view.erl b/src/chttpd_view.erl
index 6984f3e6..6d93a5b9 100644
--- a/src/chttpd_view.erl
+++ b/src/chttpd_view.erl
@@ -13,7 +13,7 @@
-module(chttpd_view).
-include("chttpd.hrl").
--export([handle_view_req/2,handle_temp_view_req/2,handle_db_view_req/2]).
+-export([handle_view_req/3,handle_temp_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]).
@@ -82,11 +82,11 @@ extract_view_type(ViewName, [View|Rest], IsReduce) ->
end.
handle_view_req(#httpd{method='GET',
- path_parts=[_Db, _Design, DName, _View, ViewName]}=Req, Db) ->
+ path_parts=[_Db, _Design, DName, _View, ViewName]}=Req, Db, DDoc) ->
design_doc_view(Req, Db, DName, ViewName, nil);
handle_view_req(#httpd{method='POST',
- path_parts=[_Db, _Design, DName, _View, ViewName]}=Req, Db) ->
+ path_parts=[_Db, _Design, DName, _View, ViewName]}=Req, Db, DDoc) ->
{Fields} = chttpd:json_body_obj(Req),
case couch_util:get_value(<<"keys">>, Fields) of
Keys when is_list(Keys) ->
@@ -95,50 +95,7 @@ handle_view_req(#httpd{method='POST',
throw({bad_request, "`keys` body member must be an array."})
end;
-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 = chttpd_view:parse_view_params(Req, nil, nil),
- #view_query_args{
- list = ListName
- } = QueryArgs,
- ?LOG_DEBUG("ici ~p", [ListName]),
- case ListName of
- nil -> chttpd_view:design_doc_view(Req, Db, DName, ViewName, nil);
- _ ->
- chttpd_show:handle_view_list(Req, DName, ListName, DName, ViewName, Db, nil)
- end;
-
-handle_db_view_req(#httpd{method='POST',
- path_parts=[_Db, _View, DName, ViewName]}=Req, Db) ->
- QueryArgs = chttpd_view:parse_view_params(Req, nil, nil),
- #view_query_args{
- list = ListName
- } = QueryArgs,
- case ListName of
- nil ->
- {Fields} = chttpd:json_body_obj(Req),
- case couch_util: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]),
- chttpd_view:design_doc_view(Req, Db, DName, ViewName, nil);
- Keys when is_list(Keys) ->
- chttpd_view:design_doc_view(Req, Db, DName, ViewName, Keys);
- _ ->
- throw({bad_request, "`keys` member must be a array."})
- end;
- _ ->
- ReqBody = chttpd:body(Req),
- {Props2} = ?JSON_DECODE(ReqBody),
- Keys = couch_util:get_value(<<"keys">>, Props2, nil),
- chttpd_show:handle_view_list(Req#httpd{req_body=ReqBody},
- DName, ListName, DName, ViewName, Db, Keys)
- end;
-
-handle_db_view_req(Req, _Db) ->
+handle_view_req(Req, _Db, _DDoc) ->
send_method_not_allowed(Req, "GET,POST,HEAD").
handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->