diff options
author | Garren Smith <garren.smith@gmail.com> | 2018-08-29 13:59:48 +0100 |
---|---|---|
committer | Robert Newson <rnewson@apache.org> | 2018-09-05 11:51:31 +0100 |
commit | c52379ced4d7da2a3c1da094d38d3887e0a49a40 (patch) | |
tree | 85f2c7eb73567b212d29ad9dfdf24e4a8f2b5cd4 | |
parent | c20e1bfa22b10d0feb94c852ba9f15dd91c268af (diff) | |
download | couchdb-c52379ced4d7da2a3c1da094d38d3887e0a49a40.tar.gz |
Add /_partition/$partition/... endpoints
-rw-r--r-- | src/chttpd/src/chttpd_db.erl | 69 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_handlers.erl | 10 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_httpd_handlers.erl | 6 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_view.erl | 17 | ||||
-rw-r--r-- | src/couch_mrview/src/couch_mrview.erl | 1 | ||||
-rw-r--r-- | src/couch_mrview/src/couch_mrview_util.erl | 14 | ||||
-rw-r--r-- | src/mango/src/mango_error.erl | 7 | ||||
-rw-r--r-- | src/mango/src/mango_httpd.erl | 85 | ||||
-rw-r--r-- | src/mango/src/mango_httpd_handlers.erl | 6 |
9 files changed, 193 insertions, 22 deletions
diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl index d1f0b811d..37289ebd6 100644 --- a/src/chttpd/src/chttpd_db.erl +++ b/src/chttpd/src/chttpd_db.erl @@ -18,7 +18,8 @@ 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/3, handle_view_cleanup_req/2, - update_doc/4, http_code_from_status/1]). + update_doc/4, http_code_from_status/1, + handle_partition_req/2]). -import(chttpd, [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2, @@ -250,21 +251,73 @@ handle_view_cleanup_req(Req, Db) -> ok = fabric:cleanup_index_files_all_nodes(Db), send_json(Req, 202, {[{ok, true}]}). + +handle_partition_req(#httpd{ + path_parts=[DbName, <<"_partition">>, Partition, _Design, Name, <<"_",_/binary>> = Action | _Rest] + }=Req, Db) -> + + validate_partition_req(Req, Partition, DbName), + DDoc = get_design_doc(DbName, Name), + Partitioned = couch_mrview:get_partitioned_opt(DDoc#doc.body, true), + + case Partitioned of + true -> + Handler = chttpd_handlers:partition_design_handler(Action, fun bad_action_partition_design_req/4), + Handler(Req, Db, DDoc, Partition); + false -> + throw({bad_request, <<"partition query is not supported in this design doc.">>}) + end; + +handle_partition_req(#httpd{ + path_parts=[DbName, <<"_partition">>, Partition, Action | _Rest] + }=Req, Db) -> + validate_partition_req(Req, Partition, DbName), + Handler = chttpd_handlers:partition_handler(Action, fun bad_action_partition_req/3), + Handler(Req, Db, Partition); + +handle_partition_req(_Req, _Db) -> + throw({bad_request, <<"missing partition key">>}). + + +bad_action_partition_design_req(Req, _Db, _DDoc, _PartitionKey) -> + chttpd:send_error(Req, 404, <<"partition_error">>, <<"Invalid path.">>). + + +bad_action_partition_req(Req, _Db, _PartitionKey) -> + chttpd:send_error(Req, 404, <<"partition_error">>, <<"Invalid path.">>). + + +validate_partition_req(_Req, Partition, DbName) -> + % validate that the partition is a valid non-partitioned doc id. + couch_doc:validate_docid(Partition, DbName), + + case mem3:is_partitioned(DbName) of + false -> throw({bad_request, <<"Database is not partitioned">>}); + true -> ok + end. + + handle_design_req(#httpd{ path_parts=[_DbName, _Design, Name, <<"_",_/binary>> = Action | _Rest] }=Req, Db) -> DbName = mem3:dbname(couch_db:name(Db)), - case ddoc_cache:open(DbName, <<"_design/", Name/binary>>) of - {ok, DDoc} -> - Handler = chttpd_handlers:design_handler(Action, fun bad_action_req/3), - Handler(Req, Db, DDoc); - Error -> - throw(Error) - end; + DDoc = get_design_doc(DbName, Name), + Handler = chttpd_handlers:design_handler(Action, fun bad_action_req/3), + Handler(Req, Db, DDoc); handle_design_req(Req, Db) -> db_req(Req, Db). + +get_design_doc(DbName, Name) -> + case ddoc_cache:open(DbName, <<"_design/", Name/binary>>) of + {ok, DDoc} -> + DDoc; + Error -> + throw(Error) + end. + + bad_action_req(#httpd{path_parts=[_, _, Name|FileNameParts]}=Req, Db, _DDoc) -> db_attachment_req(Req, Db, <<"_design/",Name/binary>>, FileNameParts). diff --git a/src/chttpd/src/chttpd_handlers.erl b/src/chttpd/src/chttpd_handlers.erl index 930563230..f2098bef2 100644 --- a/src/chttpd/src/chttpd_handlers.erl +++ b/src/chttpd/src/chttpd_handlers.erl @@ -15,7 +15,9 @@ -export([ url_handler/2, db_handler/2, - design_handler/2 + design_handler/2, + partition_handler/2, + partition_design_handler/2 ]). -define(SERVICE_ID, chttpd_handlers). @@ -35,6 +37,12 @@ db_handler(HandlerKey, DefaultFun) -> design_handler(HandlerKey, DefaultFun) -> select(collect(design_handler, [HandlerKey]), DefaultFun). +partition_handler(HandlerKey, DefaultFun) -> + select(collect(partition_handler, [HandlerKey]), DefaultFun). + +partition_design_handler(HandlerKey, DefaultFun) -> + select(collect(partition_design_handler, [HandlerKey]), DefaultFun). + %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ diff --git a/src/chttpd/src/chttpd_httpd_handlers.erl b/src/chttpd/src/chttpd_httpd_handlers.erl index cb52e2c40..2659d39f5 100644 --- a/src/chttpd/src/chttpd_httpd_handlers.erl +++ b/src/chttpd/src/chttpd_httpd_handlers.erl @@ -12,7 +12,7 @@ -module(chttpd_httpd_handlers). --export([url_handler/1, db_handler/1, design_handler/1]). +-export([url_handler/1, db_handler/1, design_handler/1, partition_design_handler/1]). url_handler(<<>>) -> fun chttpd_misc:handle_welcome_req/1; url_handler(<<"favicon.ico">>) -> fun chttpd_misc:handle_favicon_req/1; @@ -32,6 +32,7 @@ url_handler(_) -> no_match. db_handler(<<"_view_cleanup">>) -> fun chttpd_db:handle_view_cleanup_req/2; db_handler(<<"_compact">>) -> fun chttpd_db:handle_compact_req/2; db_handler(<<"_design">>) -> fun chttpd_db:handle_design_req/2; +db_handler(<<"_partition">>) -> fun chttpd_db:handle_partition_req/2; db_handler(<<"_temp_view">>) -> fun chttpd_view:handle_temp_view_req/2; db_handler(<<"_changes">>) -> fun chttpd_db:handle_changes_req/2; db_handler(_) -> no_match. @@ -43,3 +44,6 @@ design_handler(<<"_update">>) -> fun chttpd_show:handle_doc_update_req/3; design_handler(<<"_info">>) -> fun chttpd_db:handle_design_info_req/3; design_handler(<<"_rewrite">>) -> fun chttpd_rewrite:handle_rewrite_req/3; design_handler(_) -> no_match. + +partition_design_handler(<<"_view">>) -> fun chttpd_view:handle_partition_view_req/4; +partition_design_handler(_) -> no_match. diff --git a/src/chttpd/src/chttpd_view.erl b/src/chttpd/src/chttpd_view.erl index e75344f63..c658a0456 100644 --- a/src/chttpd/src/chttpd_view.erl +++ b/src/chttpd/src/chttpd_view.erl @@ -14,7 +14,7 @@ -include_lib("couch/include/couch_db.hrl"). -include_lib("couch_mrview/include/couch_mrview.hrl"). --export([handle_view_req/3, handle_temp_view_req/2]). +-export([handle_view_req/3, handle_temp_view_req/2, handle_partition_view_req/4]). multi_query_view(Req, Db, DDoc, ViewName, Queries) -> Args0 = couch_mrview_http:parse_params(Req, undefined), @@ -42,6 +42,10 @@ multi_query_view(Req, Db, DDoc, ViewName, Queries) -> design_doc_view(Req, Db, DDoc, ViewName, Keys) -> Args = couch_mrview_http:parse_params(Req, Keys), + design_doc_view_int(Req, Db, DDoc, ViewName, Args). + + +design_doc_view_int(Req, Db, DDoc, ViewName, Args) -> Max = chttpd:chunked_response_buffer_size(), VAcc = #vacc{db=Db, req=Req, threshold=Max}, Options = [{user_ctx, Req#httpd.user_ctx}], @@ -101,6 +105,17 @@ handle_temp_view_req(Req, _Db) -> chttpd:send_error(Req, 410, gone, Msg). +handle_partition_view_req(#httpd{method='GET', + path_parts=[_, _, _, _, _, _, ViewName]} = Req, Db, DDoc, Partition) -> + Keys = chttpd:qs_json_value(Req, "keys", undefined), + Args = couch_mrview_http:parse_params(Req, Keys), + Args1 = couch_mrview_util:set_extra(Args, partition, Partition), + Args2 = couch_mrview_util:set_extra(Args1, partitioned, true), + design_doc_view_int(Req, Db, DDoc, ViewName, Args2); + +handle_partition_view_req(Req, _Db, _DDoc, _Pk) -> + chttpd:send_method_not_allowed(Req, "GET"). + -ifdef(TEST). diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl index 7862afb1e..1b1a06b74 100644 --- a/src/couch_mrview/src/couch_mrview.erl +++ b/src/couch_mrview/src/couch_mrview.erl @@ -24,6 +24,7 @@ -export([refresh/2]). -export([compact/2, compact/3, cancel_compaction/2]). -export([cleanup/1]). +-export([get_partitioned_opt/2]). -include_lib("couch/include/couch_db.hrl"). -include_lib("couch_mrview/include/couch_mrview.hrl"). diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl index 794b69482..0fa5c1faa 100644 --- a/src/couch_mrview/src/couch_mrview_util.erl +++ b/src/couch_mrview/src/couch_mrview_util.erl @@ -617,6 +617,20 @@ validate_args(Args, ValidateOptions) -> ok end, + case {Partitioned, Args#mrargs.conflicts} of + {true, true} -> + mrverror(<<"`conflicts=true` is not supported in this view.">>); + {_, _} -> + ok + end, + + case {Partitioned, Args#mrargs.stable} of + {true, true} -> + mrverror(<<"`stable=true` is not supported in this view.">>); + {_, _} -> + ok + end, + Args1 = case {Style, Partitioned, Partition} of {all_docs, true, undefined} -> Args; diff --git a/src/mango/src/mango_error.erl b/src/mango/src/mango_error.erl index b2bbb392a..603fb5fd4 100644 --- a/src/mango/src/mango_error.erl +++ b/src/mango/src/mango_error.erl @@ -73,6 +73,13 @@ info(mango_fields, {invalid_field_json, BadField}) -> fmt("Invalid JSON for field spec: ~w", [BadField]) }; +info(mango_httpd, partition_field_error) -> + { + 400, + <<"bad request">>, + <<"`partition` is not a valid parameter.">> + }; + info(mango_httpd, error_saving_ddoc) -> { 500, diff --git a/src/mango/src/mango_httpd.erl b/src/mango/src/mango_httpd.erl index 2e8777135..9a5c266db 100644 --- a/src/mango/src/mango_httpd.erl +++ b/src/mango/src/mango_httpd.erl @@ -14,7 +14,8 @@ -export([ - handle_req/2 + handle_req/2, + handle_partition_req/3 ]). @@ -38,13 +39,7 @@ handle_req(#httpd{} = Req, Db0) -> handle_req_int(Req, Db) catch throw:{mango_error, Module, Reason} -> - case mango_error:info(Module, Reason) of - {500, ErrorStr, ReasonStr} -> - Stack = erlang:get_stacktrace(), - chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack}); - {Code, ErrorStr, ReasonStr} -> - chttpd:send_error(Req, Code, ErrorStr, ReasonStr) - end + handle_req_error(Req, Module, Reason) end. @@ -58,6 +53,34 @@ handle_req_int(_, _) -> throw({not_found, missing}). +handle_partition_req(#httpd{} = Req, Db0, Partition) -> + try + Db = set_user_ctx(Req, Db0), + handle_partition_req_int(Req, Db, Partition) + catch + throw:{mango_error, Module, Reason} -> + handle_req_error(Req, Module, Reason) + end. + + +handle_partition_req_int(#httpd{path_parts=[_, _, _, <<"_explain">> | _]} = Req, Db, Partition) -> + handle_partition_explain_req(Req, Db, Partition); +handle_partition_req_int(#httpd{path_parts=[_, _, _,<<"_find">> | _]} = Req, Db, Partition) -> + handle_partition_find_req(Req, Db, Partition); +handle_partition_req_int(_, _, _) -> + throw({not_found, missing}). + + +handle_req_error(Req, Module, Reason) -> + case mango_error:info(Module, Reason) of + {500, ErrorStr, ReasonStr} -> + Stack = erlang:get_stacktrace(), + chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack}); + {Code, ErrorStr, ReasonStr} -> + chttpd:send_error(Req, Code, ErrorStr, ReasonStr) + end. + + handle_index_req(#httpd{method='GET', path_parts=[_, _]}=Req, Db) -> Params = lists:flatmap(fun({K, V}) -> parse_index_param(K, V) end, chttpd:qs(Req)), @@ -170,7 +193,9 @@ handle_index_req(#httpd{path_parts=[_, _, _DDocId0, _Type, _Name]}=Req, _Db) -> handle_explain_req(#httpd{method='POST'}=Req, Db) -> chttpd:validate_ctype(Req, "application/json"), - {ok, Opts0} = mango_opts:validate_find(chttpd:json_body_obj(Req)), + {Body0} = chttpd:json_body_obj(Req), + check_for_partition_param(Body0), + {ok, Opts0} = mango_opts:validate_find({Body0}), {value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0), Resp = mango_crud:explain(Db, Sel, Opts), chttpd:send_json(Req, Resp); @@ -179,9 +204,23 @@ handle_explain_req(Req, _Db) -> chttpd:send_method_not_allowed(Req, "POST"). +handle_partition_explain_req(#httpd{method='POST'}=Req, Db, Partition) -> + chttpd:validate_ctype(Req, "application/json"), + {ok, Body} = add_partition_to_query(Req, Partition), + {ok, Opts0} = mango_opts:validate_find(Body), + {value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0), + Resp = mango_crud:explain(Db, Sel, Opts), + chttpd:send_json(Req, Resp); + +handle_partition_explain_req(Req, _Db, _Partition) -> + chttpd:send_method_not_allowed(Req, "POST"). + + handle_find_req(#httpd{method='POST'}=Req, Db) -> chttpd:validate_ctype(Req, "application/json"), - {ok, Opts0} = mango_opts:validate_find(chttpd:json_body_obj(Req)), + {Body0} = chttpd:json_body_obj(Req), + check_for_partition_param(Body0), + {ok, Opts0} = mango_opts:validate_find({Body0}), {value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0), {ok, Resp0} = start_find_resp(Req), {ok, AccOut} = run_find(Resp0, Db, Sel, Opts), @@ -191,6 +230,32 @@ handle_find_req(Req, _Db) -> chttpd:send_method_not_allowed(Req, "POST"). +handle_partition_find_req(#httpd{method='POST'}=Req, Db, Partition) -> + chttpd:validate_ctype(Req, "application/json"), + {ok, Body} = add_partition_to_query(Req, Partition), + {ok, Opts0} = mango_opts:validate_find(Body), + {value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0), + {ok, Resp0} = start_find_resp(Req), + {ok, AccOut} = run_find(Resp0, Db, Sel, Opts), + end_find_resp(AccOut); + +handle_partition_find_req(Req, _Db, _Partition) -> + chttpd:send_method_not_allowed(Req, "POST"). + +check_for_partition_param(Body) -> + case lists:keyfind(<<"partition">>, 1, Body) of + false -> ok; + _ -> ?MANGO_ERROR(partition_field_error) + end. + + +add_partition_to_query(Req, Partition) -> + {Body0} = chttpd:json_body_obj(Req), + check_for_partition_param(Body0), + Body1 = [{<<"partition">>, Partition} | Body0], + {ok, {Body1}}. + + set_user_ctx(#httpd{user_ctx=Ctx}, Db) -> {ok, NewDb} = couch_db:set_user_ctx(Db, Ctx), NewDb. diff --git a/src/mango/src/mango_httpd_handlers.erl b/src/mango/src/mango_httpd_handlers.erl index 80e5e277e..8589b7e14 100644 --- a/src/mango/src/mango_httpd_handlers.erl +++ b/src/mango/src/mango_httpd_handlers.erl @@ -12,7 +12,7 @@ -module(mango_httpd_handlers). --export([url_handler/1, db_handler/1, design_handler/1]). +-export([url_handler/1, db_handler/1, design_handler/1, partition_handler/1]). url_handler(_) -> no_match. @@ -22,3 +22,7 @@ db_handler(<<"_find">>) -> fun mango_httpd:handle_req/2; db_handler(_) -> no_match. design_handler(_) -> no_match. + +partition_handler(<<"_find">>) -> fun mango_httpd:handle_partition_req/3; +partition_handler(<<"_explain">>) -> fun mango_httpd:handle_partition_req/3; +partition_handler(_) -> no_match. |