summaryrefslogtreecommitdiff
path: root/src/couch_mrview/src/couch_mrview_http.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch_mrview/src/couch_mrview_http.erl')
-rw-r--r--src/couch_mrview/src/couch_mrview_http.erl650
1 files changed, 0 insertions, 650 deletions
diff --git a/src/couch_mrview/src/couch_mrview_http.erl b/src/couch_mrview/src/couch_mrview_http.erl
deleted file mode 100644
index e1ba9d656..000000000
--- a/src/couch_mrview/src/couch_mrview_http.erl
+++ /dev/null
@@ -1,650 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_http).
-
--export([
- handle_all_docs_req/2,
- handle_local_docs_req/2,
- handle_design_docs_req/2,
- handle_reindex_req/3,
- handle_view_req/3,
- handle_temp_view_req/2,
- handle_info_req/3,
- handle_compact_req/3,
- handle_cleanup_req/2
-]).
-
--export([
- parse_boolean/1,
- parse_int/1,
- parse_pos_int/1,
- prepend_val/1,
- parse_body_and_query/2,
- parse_body_and_query/3,
- parse_params/2,
- parse_params/3,
- parse_params/4,
- view_cb/2,
- row_to_obj/1,
- row_to_obj/2,
- row_to_json/1,
- row_to_json/2,
- check_view_etag/3
-]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
-
-handle_all_docs_req(#httpd{method='GET'}=Req, Db) ->
- all_docs_req(Req, Db, undefined);
-handle_all_docs_req(#httpd{method='POST'}=Req, Db) ->
- chttpd:validate_ctype(Req, "application/json"),
- Keys = couch_mrview_util:get_view_keys(chttpd:json_body_obj(Req)),
- all_docs_req(Req, Db, Keys);
-handle_all_docs_req(Req, _Db) ->
- chttpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-handle_local_docs_req(#httpd{method='GET'}=Req, Db) ->
- all_docs_req(Req, Db, undefined, <<"_local">>);
-handle_local_docs_req(#httpd{method='POST'}=Req, Db) ->
- chttpd:validate_ctype(Req, "application/json"),
- Keys = couch_mrview_util:get_view_keys(chttpd:json_body_obj(Req)),
- all_docs_req(Req, Db, Keys, <<"_local">>);
-handle_local_docs_req(Req, _Db) ->
- chttpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-handle_design_docs_req(#httpd{method='GET'}=Req, Db) ->
- all_docs_req(Req, Db, undefined, <<"_design">>);
-handle_design_docs_req(#httpd{method='POST'}=Req, Db) ->
- chttpd:validate_ctype(Req, "application/json"),
- Keys = couch_mrview_util:get_view_keys(chttpd:json_body_obj(Req)),
- all_docs_req(Req, Db, Keys, <<"_design">>);
-handle_design_docs_req(Req, _Db) ->
- chttpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-handle_reindex_req(#httpd{method='POST',
- path_parts=[_, _, DName,<<"_reindex">>]}=Req,
- Db, _DDoc) ->
- chttpd:validate_ctype(Req, "application/json"),
- ok = couch_db:check_is_admin(Db),
- couch_mrview:trigger_update(Db, <<"_design/", DName/binary>>),
- chttpd:send_json(Req, 201, {[{<<"ok">>, true}]});
-handle_reindex_req(Req, _Db, _DDoc) ->
- chttpd:send_method_not_allowed(Req, "POST").
-
-
-handle_view_req(#httpd{method='GET',
- path_parts=[_, _, DDocName, _, VName, <<"_info">>]}=Req,
- Db, _DDoc) ->
- DbName = couch_db:name(Db),
- DDocId = <<"_design/", DDocName/binary >>,
- {ok, Info} = couch_mrview:get_view_info(DbName, DDocId, VName),
-
- FinalInfo = [{db_name, DbName},
- {ddoc, DDocId},
- {view, VName}] ++ Info,
- chttpd:send_json(Req, 200, {FinalInfo});
-handle_view_req(#httpd{method='GET'}=Req, Db, DDoc) ->
- [_, _, _, _, ViewName] = Req#httpd.path_parts,
- couch_stats:increment_counter([couchdb, httpd, view_reads]),
- design_doc_view(Req, Db, DDoc, ViewName, undefined);
-handle_view_req(#httpd{method='POST'}=Req, Db, DDoc) ->
- chttpd:validate_ctype(Req, "application/json"),
- [_, _, _, _, ViewName] = Req#httpd.path_parts,
- Props = chttpd:json_body_obj(Req),
- Keys = couch_mrview_util:get_view_keys(Props),
- Queries = couch_mrview_util:get_view_queries(Props),
- case {Queries, Keys} of
- {Queries, undefined} when is_list(Queries) ->
- IncrBy = length(Queries),
- couch_stats:increment_counter([couchdb, httpd, view_reads], IncrBy),
- multi_query_view(Req, Db, DDoc, ViewName, Queries);
- {undefined, Keys} when is_list(Keys) ->
- couch_stats:increment_counter([couchdb, httpd, view_reads]),
- design_doc_view(Req, Db, DDoc, ViewName, Keys);
- {undefined, undefined} ->
- throw({
- bad_request,
- "POST body must contain `keys` or `queries` field"
- });
- {_, _} ->
- throw({bad_request, "`keys` and `queries` are mutually exclusive"})
- end;
-handle_view_req(Req, _Db, _DDoc) ->
- chttpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-
-handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
- chttpd:validate_ctype(Req, "application/json"),
- ok = couch_db:check_is_admin(Db),
- {Body} = chttpd:json_body_obj(Req),
- DDoc = couch_mrview_util:temp_view_to_ddoc({Body}),
- Keys = couch_mrview_util:get_view_keys({Body}),
- couch_stats:increment_counter([couchdb, httpd, temporary_view_reads]),
- design_doc_view(Req, Db, DDoc, <<"temp">>, Keys);
-handle_temp_view_req(Req, _Db) ->
- chttpd:send_method_not_allowed(Req, "POST").
-
-
-handle_info_req(#httpd{method='GET'}=Req, Db, DDoc) ->
- [_, _, Name, _] = Req#httpd.path_parts,
- {ok, Info} = couch_mrview:get_info(Db, DDoc),
- chttpd:send_json(Req, 200, {[
- {name, Name},
- {view_index, {Info}}
- ]});
-handle_info_req(Req, _Db, _DDoc) ->
- chttpd:send_method_not_allowed(Req, "GET").
-
-
-handle_compact_req(#httpd{method='POST'}=Req, Db, DDoc) ->
- chttpd:validate_ctype(Req, "application/json"),
- ok = couch_db:check_is_admin(Db),
- ok = couch_mrview:compact(Db, DDoc),
- chttpd:send_json(Req, 202, {[{ok, true}]});
-handle_compact_req(Req, _Db, _DDoc) ->
- chttpd:send_method_not_allowed(Req, "POST").
-
-
-handle_cleanup_req(#httpd{method='POST'}=Req, Db) ->
- chttpd:validate_ctype(Req, "application/json"),
- ok = couch_db:check_is_admin(Db),
- ok = couch_mrview:cleanup(Db),
- chttpd:send_json(Req, 202, {[{ok, true}]});
-handle_cleanup_req(Req, _Db) ->
- chttpd:send_method_not_allowed(Req, "POST").
-
-
-all_docs_req(Req, Db, Keys) ->
- all_docs_req(Req, Db, Keys, undefined).
-
-all_docs_req(Req, Db, Keys, NS) ->
- case is_restricted(Db, NS) of
- true ->
- case (catch couch_db:check_is_admin(Db)) of
- ok ->
- do_all_docs_req(Req, Db, Keys, NS);
- _ when NS == <<"_local">> ->
- throw({forbidden, <<"Only admins can access _local_docs">>});
- _ ->
- case is_public_fields_configured(Db) of
- true ->
- do_all_docs_req(Req, Db, Keys, NS);
- false ->
- throw({forbidden, <<"Only admins can access _all_docs",
- " of system databases.">>})
- end
- end;
- false ->
- do_all_docs_req(Req, Db, Keys, NS)
- end.
-
-is_restricted(_Db, <<"_local">>) ->
- true;
-is_restricted(Db, _) ->
- couch_db:is_system_db(Db).
-
-is_public_fields_configured(Db) ->
- DbName = ?b2l(couch_db:name(Db)),
- case config:get("couch_httpd_auth", "authentication_db", "_users") of
- DbName ->
- UsersDbPublic = config:get("couch_httpd_auth", "users_db_public", "false"),
- PublicFields = config:get("couch_httpd_auth", "public_fields"),
- case {UsersDbPublic, PublicFields} of
- {"true", PublicFields} when PublicFields =/= undefined ->
- true;
- {_, _} ->
- false
- end;
- _ ->
- false
- end.
-
-do_all_docs_req(Req, Db, Keys, NS) ->
- Args0 = couch_mrview_http:parse_body_and_query(Req, Keys),
- Args1 = set_namespace(NS, Args0),
- ETagFun = fun(Sig, Acc0) ->
- check_view_etag(Sig, Acc0, Req)
- end,
- Args = Args1#mrargs{preflight_fun=ETagFun},
- {ok, Resp} = couch_httpd:etag_maybe(Req, fun() ->
- Max = chttpd:chunked_response_buffer_size(),
- VAcc0 = #vacc{db=Db, req=Req, threshold=Max},
- DbName = ?b2l(couch_db:name(Db)),
- UsersDbName = config:get("couch_httpd_auth",
- "authentication_db",
- "_users"),
- IsAdmin = is_admin(Db),
- Callback = get_view_callback(DbName, UsersDbName, IsAdmin),
- couch_mrview:query_all_docs(Db, Args, Callback, VAcc0)
- end),
- case is_record(Resp, vacc) of
- true -> {ok, Resp#vacc.resp};
- _ -> {ok, Resp}
- end.
-
-set_namespace(NS, #mrargs{extra = Extra} = Args) ->
- Args#mrargs{extra = [{namespace, NS} | Extra]}.
-
-is_admin(Db) ->
- case catch couch_db:check_is_admin(Db) of
- {unauthorized, _} ->
- false;
- ok ->
- true
- end.
-
-
-% admin users always get all fields
-get_view_callback(_, _, true) ->
- fun view_cb/2;
-% if we are operating on the users db and we aren't
-% admin, filter the view
-get_view_callback(_DbName, _DbName, false) ->
- fun filtered_view_cb/2;
-% non _users databases get all fields
-get_view_callback(_, _, _) ->
- fun view_cb/2.
-
-
-design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
- Args0 = parse_params(Req, Keys),
- ETagFun = fun(Sig, Acc0) ->
- check_view_etag(Sig, Acc0, Req)
- end,
- Args = Args0#mrargs{preflight_fun=ETagFun},
- {ok, Resp} = couch_httpd:etag_maybe(Req, fun() ->
- Max = chttpd:chunked_response_buffer_size(),
- VAcc0 = #vacc{db=Db, req=Req, threshold=Max},
- couch_mrview:query_view(Db, DDoc, ViewName, Args, fun view_cb/2, VAcc0)
- end),
- case is_record(Resp, vacc) of
- true -> {ok, Resp#vacc.resp};
- _ -> {ok, Resp}
- end.
-
-
-multi_query_view(Req, Db, DDoc, ViewName, Queries) ->
- Args0 = parse_params(Req, undefined),
- {ok, _, _, Args1} = couch_mrview_util:get_view(Db, DDoc, ViewName, Args0),
- ArgQueries = lists:map(fun({Query}) ->
- QueryArg = parse_params(Query, undefined, Args1),
- couch_mrview_util:validate_args(Db, DDoc, QueryArg)
- end, Queries),
- {ok, Resp2} = couch_httpd:etag_maybe(Req, fun() ->
- Max = chttpd:chunked_response_buffer_size(),
- VAcc0 = #vacc{db=Db, req=Req, prepend="\r\n", threshold=Max},
- %% TODO: proper calculation of etag
- Etag = [$", couch_uuids:new(), $"],
- Headers = [{"ETag", Etag}],
- FirstChunk = "{\"results\":[",
- {ok, Resp0} = chttpd:start_delayed_json_response(VAcc0#vacc.req, 200, Headers, FirstChunk),
- VAcc1 = VAcc0#vacc{resp=Resp0},
- VAcc2 = lists:foldl(fun(Args, Acc0) ->
- {ok, Acc1} = couch_mrview:query_view(Db, DDoc, ViewName, Args, fun view_cb/2, Acc0),
- Acc1
- end, VAcc1, ArgQueries),
- {ok, Resp1} = chttpd:send_delayed_chunk(VAcc2#vacc.resp, "\r\n]}"),
- {ok, Resp2} = chttpd:end_delayed_json_response(Resp1),
- {ok, VAcc2#vacc{resp=Resp2}}
- end),
- case is_record(Resp2, vacc) of
- true -> {ok, Resp2#vacc.resp};
- _ -> {ok, Resp2}
- end.
-
-filtered_view_cb({row, Row0}, Acc) ->
- Row1 = lists:map(fun({doc, null}) ->
- {doc, null};
- ({doc, Body}) ->
- Doc = couch_users_db:strip_non_public_fields(#doc{body=Body}),
- {doc, Doc#doc.body};
- (KV) ->
- KV
- end, Row0),
- view_cb({row, Row1}, Acc);
-filtered_view_cb(Obj, Acc) ->
- view_cb(Obj, Acc).
-
-
-%% these clauses start (and possibly end) the response
-view_cb({error, Reason}, #vacc{resp=undefined}=Acc) ->
- {ok, Resp} = chttpd:send_error(Acc#vacc.req, Reason),
- {ok, Acc#vacc{resp=Resp}};
-
-view_cb(complete, #vacc{resp=undefined}=Acc) ->
- % Nothing in view
- {ok, Resp} = chttpd:send_json(Acc#vacc.req, 200, {[{rows, []}]}),
- {ok, Acc#vacc{resp=Resp}};
-
-view_cb(Msg, #vacc{resp=undefined}=Acc) ->
- %% Start response
- Headers = [],
- {ok, Resp} = chttpd:start_delayed_json_response(Acc#vacc.req, 200, Headers),
- view_cb(Msg, Acc#vacc{resp=Resp, should_close=true});
-
-%% ---------------------------------------------------
-
-%% From here on down, the response has been started.
-
-view_cb({error, Reason}, #vacc{resp=Resp}=Acc) ->
- {ok, Resp1} = chttpd:send_delayed_error(Resp, Reason),
- {ok, Acc#vacc{resp=Resp1}};
-
-view_cb(complete, #vacc{resp=Resp, buffer=Buf, threshold=Max}=Acc) ->
- % Finish view output and possibly end the response
- {ok, Resp1} = chttpd:close_delayed_json_object(Resp, Buf, "\r\n]}", Max),
- case Acc#vacc.should_close of
- true ->
- {ok, Resp2} = chttpd:end_delayed_json_response(Resp1),
- {ok, Acc#vacc{resp=Resp2}};
- _ ->
- {ok, Acc#vacc{resp=Resp1, meta_sent=false, row_sent=false,
- prepend=",\r\n", buffer=[], bufsize=0}}
- end;
-
-view_cb({meta, Meta}, #vacc{meta_sent=false, row_sent=false}=Acc) ->
- % Sending metadata as we've not sent it or any row yet
- Parts = case couch_util:get_value(total, Meta) of
- undefined -> [];
- Total -> [io_lib:format("\"total_rows\":~p", [Total])]
- end ++ case couch_util:get_value(offset, Meta) of
- undefined -> [];
- Offset -> [io_lib:format("\"offset\":~p", [Offset])]
- end ++ case couch_util:get_value(update_seq, Meta) of
- undefined -> [];
- null ->
- ["\"update_seq\":null"];
- UpdateSeq when is_integer(UpdateSeq) ->
- [io_lib:format("\"update_seq\":~B", [UpdateSeq])];
- UpdateSeq when is_binary(UpdateSeq) ->
- [io_lib:format("\"update_seq\":\"~s\"", [UpdateSeq])]
- end ++ ["\"rows\":["],
- Chunk = [prepend_val(Acc), "{", string:join(Parts, ","), "\r\n"],
- {ok, AccOut} = maybe_flush_response(Acc, Chunk, iolist_size(Chunk)),
- {ok, AccOut#vacc{prepend="", meta_sent=true}};
-
-view_cb({meta, _Meta}, #vacc{}=Acc) ->
- %% ignore metadata
- {ok, Acc};
-
-view_cb({row, Row}, #vacc{meta_sent=false}=Acc) ->
- %% sorted=false and row arrived before meta
- % Adding another row
- Chunk = [prepend_val(Acc), "{\"rows\":[\r\n", row_to_json(Row)],
- maybe_flush_response(Acc#vacc{meta_sent=true, row_sent=true}, Chunk, iolist_size(Chunk));
-
-view_cb({row, Row}, #vacc{meta_sent=true}=Acc) ->
- % Adding another row
- Chunk = [prepend_val(Acc), row_to_json(Row)],
- maybe_flush_response(Acc#vacc{row_sent=true}, Chunk, iolist_size(Chunk)).
-
-
-maybe_flush_response(#vacc{bufsize=Size, threshold=Max} = Acc, Data, Len)
- when Size > 0 andalso (Size + Len) > Max ->
- #vacc{buffer = Buffer, resp = Resp} = Acc,
- {ok, R1} = chttpd:send_delayed_chunk(Resp, Buffer),
- {ok, Acc#vacc{prepend = ",\r\n", buffer = Data, bufsize = Len, resp = R1}};
-maybe_flush_response(Acc0, Data, Len) ->
- #vacc{buffer = Buf, bufsize = Size} = Acc0,
- Acc = Acc0#vacc{
- prepend = ",\r\n",
- buffer = [Buf | Data],
- bufsize = Size + Len
- },
- {ok, Acc}.
-
-prepend_val(#vacc{prepend=Prepend}) ->
- case Prepend of
- undefined ->
- "";
- _ ->
- Prepend
- end.
-
-
-row_to_json(Row) ->
- ?JSON_ENCODE(row_to_obj(Row)).
-
-
-row_to_json(Kind, Row) ->
- ?JSON_ENCODE(row_to_obj(Kind, Row)).
-
-
-row_to_obj(Row) ->
- Id = couch_util:get_value(id, Row),
- row_to_obj(Id, Row).
-
-
-row_to_obj(error, Row) ->
- % Special case for _all_docs request with KEYS to
- % match prior behavior.
- Key = couch_util:get_value(key, Row),
- Val = couch_util:get_value(value, Row),
- Reason = couch_util:get_value(reason, Row),
- ReasonProp = if Reason == undefined -> []; true ->
- [{reason, Reason}]
- end,
- {[{key, Key}, {error, Val}] ++ ReasonProp};
-row_to_obj(Id0, Row) ->
- Id = case Id0 of
- undefined -> [];
- Id0 -> [{id, Id0}]
- end,
- Key = couch_util:get_value(key, Row, null),
- Val = couch_util:get_value(value, Row),
- Doc = case couch_util:get_value(doc, Row) of
- undefined -> [];
- Doc0 -> [{doc, Doc0}]
- end,
- {Id ++ [{key, Key}, {value, Val}] ++ Doc}.
-
-
-parse_params(#httpd{}=Req, Keys) ->
- parse_params(chttpd:qs(Req), Keys);
-parse_params(Props, Keys) ->
- Args = #mrargs{},
- parse_params(Props, Keys, Args).
-
-
-parse_params(Props, Keys, Args) ->
- parse_params(Props, Keys, Args, []).
-
-parse_params(Props, Keys, #mrargs{}=Args0, Options) ->
- IsDecoded = lists:member(decoded, Options),
- Args1 = case lists:member(keep_group_level, Options) of
- true ->
- Args0;
- _ ->
- % group_level set to undefined to detect if explicitly set by user
- Args0#mrargs{keys=Keys, group=undefined, group_level=undefined}
- end,
- lists:foldl(fun({K, V}, Acc) ->
- parse_param(K, V, Acc, IsDecoded)
- end, Args1, Props).
-
-
-parse_body_and_query(#httpd{method='POST'} = Req, Keys) ->
- Props = chttpd:json_body_obj(Req),
- parse_body_and_query(Req, Props, Keys);
-
-parse_body_and_query(Req, Keys) ->
- parse_params(chttpd:qs(Req), Keys, #mrargs{keys=Keys, group=undefined,
- group_level=undefined}, [keep_group_level]).
-
-parse_body_and_query(Req, {Props}, Keys) ->
- Args = #mrargs{keys=Keys, group=undefined, group_level=undefined},
- BodyArgs = parse_params(Props, Keys, Args, [decoded]),
- parse_params(chttpd:qs(Req), Keys, BodyArgs, [keep_group_level]).
-
-parse_param(Key, Val, Args, IsDecoded) when is_binary(Key) ->
- parse_param(binary_to_list(Key), Val, Args, IsDecoded);
-parse_param(Key, Val, Args, IsDecoded) ->
- case Key of
- "" ->
- Args;
- "reduce" ->
- Args#mrargs{reduce=parse_boolean(Val)};
- "key" when IsDecoded ->
- Args#mrargs{start_key=Val, end_key=Val};
- "key" ->
- JsonKey = ?JSON_DECODE(Val),
- Args#mrargs{start_key=JsonKey, end_key=JsonKey};
- "keys" when IsDecoded ->
- Args#mrargs{keys=Val};
- "keys" ->
- Args#mrargs{keys=?JSON_DECODE(Val)};
- "startkey" when IsDecoded ->
- Args#mrargs{start_key=Val};
- "start_key" when IsDecoded ->
- Args#mrargs{start_key=Val};
- "startkey" ->
- Args#mrargs{start_key=?JSON_DECODE(Val)};
- "start_key" ->
- Args#mrargs{start_key=?JSON_DECODE(Val)};
- "startkey_docid" ->
- Args#mrargs{start_key_docid=couch_util:to_binary(Val)};
- "start_key_doc_id" ->
- Args#mrargs{start_key_docid=couch_util:to_binary(Val)};
- "endkey" when IsDecoded ->
- Args#mrargs{end_key=Val};
- "end_key" when IsDecoded ->
- Args#mrargs{end_key=Val};
- "endkey" ->
- Args#mrargs{end_key=?JSON_DECODE(Val)};
- "end_key" ->
- Args#mrargs{end_key=?JSON_DECODE(Val)};
- "endkey_docid" ->
- Args#mrargs{end_key_docid=couch_util:to_binary(Val)};
- "end_key_doc_id" ->
- Args#mrargs{end_key_docid=couch_util:to_binary(Val)};
- "limit" ->
- Args#mrargs{limit=parse_pos_int(Val)};
- "page_size" ->
- Args#mrargs{page_size=parse_pos_int(Val)};
- "stale" when Val == "ok" orelse Val == <<"ok">> ->
- Args#mrargs{stable=true, update=false};
- "stale" when Val == "update_after" orelse Val == <<"update_after">> ->
- Args#mrargs{stable=true, update=lazy};
- "stale" ->
- throw({query_parse_error, <<"Invalid value for `stale`.">>});
- "stable" when Val == "true" orelse Val == <<"true">> ->
- Args#mrargs{stable=true};
- "stable" when Val == "false" orelse Val == <<"false">> ->
- Args#mrargs{stable=false};
- "stable" ->
- throw({query_parse_error, <<"Invalid value for `stable`.">>});
- "update" when Val == "true" orelse Val == <<"true">> ->
- Args#mrargs{update=true};
- "update" when Val == "false" orelse Val == <<"false">> ->
- Args#mrargs{update=false};
- "update" when Val == "lazy" orelse Val == <<"lazy">> ->
- Args#mrargs{update=lazy};
- "update" ->
- throw({query_parse_error, <<"Invalid value for `update`.">>});
- "descending" ->
- case parse_boolean(Val) of
- true -> Args#mrargs{direction=rev};
- _ -> Args#mrargs{direction=fwd}
- end;
- "skip" ->
- Args#mrargs{skip=parse_pos_int(Val)};
- "group" ->
- Args#mrargs{group=parse_boolean(Val)};
- "group_level" ->
- Args#mrargs{group_level=parse_pos_int(Val)};
- "inclusive_end" ->
- Args#mrargs{inclusive_end=parse_boolean(Val)};
- "include_docs" ->
- Args#mrargs{include_docs=parse_boolean(Val)};
- "attachments" ->
- case parse_boolean(Val) of
- true ->
- Opts = Args#mrargs.doc_options,
- Args#mrargs{doc_options=[attachments|Opts]};
- false ->
- Args
- end;
- "att_encoding_info" ->
- case parse_boolean(Val) of
- true ->
- Opts = Args#mrargs.doc_options,
- Args#mrargs{doc_options=[att_encoding_info|Opts]};
- false ->
- Args
- end;
- "update_seq" ->
- Args#mrargs{update_seq=parse_boolean(Val)};
- "conflicts" ->
- Args#mrargs{conflicts=parse_boolean(Val)};
- "callback" ->
- Args#mrargs{callback=couch_util:to_binary(Val)};
- "sorted" ->
- Args#mrargs{sorted=parse_boolean(Val)};
- "partition" ->
- Partition = couch_util:to_binary(Val),
- couch_partition:validate_partition(Partition),
- couch_mrview_util:set_extra(Args, partition, Partition);
- _ ->
- BKey = couch_util:to_binary(Key),
- BVal = couch_util:to_binary(Val),
- Args#mrargs{extra=[{BKey, BVal} | Args#mrargs.extra]}
- end.
-
-
-parse_boolean(true) ->
- true;
-parse_boolean(false) ->
- false;
-
-parse_boolean(Val) when is_binary(Val) ->
- parse_boolean(?b2l(Val));
-
-parse_boolean(Val) ->
- case string:to_lower(Val) of
- "true" -> true;
- "false" -> false;
- _ ->
- Msg = io_lib:format("Invalid boolean parameter: ~p", [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
-
-parse_int(Val) when is_integer(Val) ->
- Val;
-parse_int(Val) ->
- case (catch list_to_integer(Val)) of
- IntVal when is_integer(IntVal) ->
- IntVal;
- _ ->
- Msg = io_lib:format("Invalid value for integer: ~p", [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
-
-parse_pos_int(Val) ->
- case parse_int(Val) of
- IntVal when IntVal >= 0 ->
- IntVal;
- _ ->
- Fmt = "Invalid value for positive integer: ~p",
- Msg = io_lib:format(Fmt, [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
-
-
-check_view_etag(Sig, Acc0, Req) ->
- ETag = chttpd:make_etag(Sig),
- case chttpd:etag_match(Req, ETag) of
- true -> throw({etag_match, ETag});
- false -> {ok, Acc0#vacc{etag=ETag}}
- end.