summaryrefslogtreecommitdiff
path: root/apps/chttpd/src/chttpd_view.erl
diff options
context:
space:
mode:
Diffstat (limited to 'apps/chttpd/src/chttpd_view.erl')
-rw-r--r--apps/chttpd/src/chttpd_view.erl306
1 files changed, 0 insertions, 306 deletions
diff --git a/apps/chttpd/src/chttpd_view.erl b/apps/chttpd/src/chttpd_view.erl
deleted file mode 100644
index 89f91cb2..00000000
--- a/apps/chttpd/src/chttpd_view.erl
+++ /dev/null
@@ -1,306 +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(chttpd_view).
--include_lib("couch/include/couch_db.hrl").
-
--export([handle_view_req/3, handle_temp_view_req/2, get_reduce_type/1,
- parse_view_params/3, view_group_etag/2, view_group_etag/3,
- parse_bool_param/1, extract_view_type/3]).
-
--import(chttpd,
- [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,send_chunk/2,
- start_json_response/2, start_json_response/3, end_json_response/1,
- send_chunked_error/2]).
-
-design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
- Group = couch_view_group:design_doc_to_view_group(DDoc),
- IsReduce = get_reduce_type(Req),
- ViewType = extract_view_type(ViewName, Group#group.views, IsReduce),
- QueryArgs = parse_view_params(Req, Keys, ViewType),
- % TODO proper calculation of etag
- % Etag = view_group_etag(ViewGroup, Db, Keys),
- Etag = couch_uuids:new(),
- couch_stats_collector:increment({httpd, view_reads}),
- chttpd:etag_respond(Req, Etag, fun() ->
- {ok, Resp} = chttpd:start_json_response(Req, 200, [{"Etag",Etag}]),
- CB = fun view_callback/2,
- fabric:query_view(Db, DDoc, ViewName, CB, {nil, Resp}, QueryArgs)
- end).
-
-view_callback({total_and_offset, Total, Offset}, {nil, Resp}) ->
- Chunk = "{\"total_rows\":~p,\"offset\":~p,\"rows\":[\r\n",
- send_chunk(Resp, io_lib:format(Chunk, [Total, Offset])),
- {ok, {"", Resp}};
-view_callback({total_and_offset, _, _}, Acc) ->
- % a sorted=false view where the message came in late. Ignore.
- {ok, Acc};
-view_callback({row, Row}, {nil, Resp}) ->
- % first row of a reduce view, or a sorted=false view
- send_chunk(Resp, ["{\"rows\":[\r\n", ?JSON_ENCODE(Row)]),
- {ok, {",\r\n", Resp}};
-view_callback({row, Row}, {Prepend, Resp}) ->
- send_chunk(Resp, [Prepend, ?JSON_ENCODE(Row)]),
- {ok, {",\r\n", Resp}};
-view_callback(complete, {nil, Resp}) ->
- send_chunk(Resp, "{\"rows\":[]}"),
- end_json_response(Resp),
- {ok, Resp};
-view_callback(complete, {_, Resp}) ->
- send_chunk(Resp, "\r\n]}"),
- end_json_response(Resp),
- {ok, Resp};
-view_callback({error, Reason}, {_, Resp}) ->
- chttpd:send_chunked_error(Resp, {error, Reason}).
-
-extract_view_type(_ViewName, [], _IsReduce) ->
- throw({not_found, missing_named_view});
-extract_view_type(ViewName, [View|Rest], IsReduce) ->
- case lists:member(ViewName, [Name || {Name, _} <- View#view.reduce_funs]) of
- true ->
- if IsReduce -> reduce; true -> red_map end;
- false ->
- case lists:member(ViewName, View#view.map_names) of
- true -> map;
- false -> extract_view_type(ViewName, Rest, IsReduce)
- end
- end.
-
-handle_view_req(#httpd{method='GET',
- path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) ->
- design_doc_view(Req, Db, DDoc, ViewName, nil);
-
-handle_view_req(#httpd{method='POST',
- path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) ->
- {Fields} = chttpd:json_body_obj(Req),
- case couch_util:get_value(<<"keys">>, Fields) of
- Keys when is_list(Keys) ->
- design_doc_view(Req, Db, DDoc, ViewName, Keys);
- _ ->
- throw({bad_request, "`keys` body member must be an array."})
- end;
-
-handle_view_req(Req, _Db, _DDoc) ->
- send_method_not_allowed(Req, "GET,POST,HEAD").
-
-handle_temp_view_req(Req, _Db) ->
- Msg = <<"Temporary views are not supported in BigCouch">>,
- chttpd:send_error(Req, 403, forbidden, Msg).
-
-reverse_key_default(?MIN_STR) -> ?MAX_STR;
-reverse_key_default(?MAX_STR) -> ?MIN_STR;
-reverse_key_default(Key) -> Key.
-
-get_reduce_type(Req) ->
- list_to_atom(chttpd:qs_value(Req, "reduce", "true")).
-
-parse_view_params(Req, Keys, ViewType) ->
- QueryList = chttpd:qs(Req),
- QueryParams =
- lists:foldl(fun({K, V}, Acc) ->
- parse_view_param(K, V) ++ Acc
- end, [], QueryList),
- IsMultiGet = case Keys of
- nil -> false;
- _ -> true
- end,
- Args = #view_query_args{
- view_type=ViewType,
- multi_get=IsMultiGet,
- keys=Keys
- },
- QueryArgs = lists:foldl(fun({K, V}, Args2) ->
- validate_view_query(K, V, Args2)
- end, Args, lists:reverse(QueryParams)), % Reverse to match QS order.
-
- GroupLevel = QueryArgs#view_query_args.group_level,
- case {ViewType, GroupLevel, IsMultiGet} of
- {reduce, exact, true} ->
- QueryArgs;
- {reduce, _, false} ->
- QueryArgs;
- {reduce, _, _} ->
- Msg = <<"Multi-key fetchs for reduce "
- "view must include `group=true`">>,
- throw({query_parse_error, Msg});
- _ ->
- QueryArgs
- end,
- QueryArgs.
-
-parse_view_param("", _) ->
- [];
-parse_view_param("key", Value) ->
- JsonKey = ?JSON_DECODE(Value),
- [{start_key, JsonKey}, {end_key, JsonKey}];
-parse_view_param("startkey_docid", Value) ->
- [{start_docid, ?l2b(Value)}];
-parse_view_param("endkey_docid", Value) ->
- [{end_docid, ?l2b(Value)}];
-parse_view_param("startkey", Value) ->
- [{start_key, ?JSON_DECODE(Value)}];
-parse_view_param("endkey", Value) ->
- [{end_key, ?JSON_DECODE(Value)}];
-parse_view_param("limit", Value) ->
- [{limit, parse_positive_int_param(Value)}];
-parse_view_param("count", _Value) ->
- throw({query_parse_error, <<"Query parameter 'count' is now 'limit'.">>});
-parse_view_param("stale", "ok") ->
- [{stale, ok}];
-parse_view_param("stale", _Value) ->
- throw({query_parse_error, <<"stale only available as stale=ok">>});
-parse_view_param("update", _Value) ->
- throw({query_parse_error, <<"update=false is now stale=ok">>});
-parse_view_param("descending", Value) ->
- [{descending, parse_bool_param(Value)}];
-parse_view_param("skip", Value) ->
- [{skip, parse_int_param(Value)}];
-parse_view_param("group", Value) ->
- case parse_bool_param(Value) of
- true -> [{group_level, exact}];
- false -> [{group_level, 0}]
- end;
-parse_view_param("group_level", Value) ->
- [{group_level, parse_positive_int_param(Value)}];
-parse_view_param("inclusive_end", Value) ->
- [{inclusive_end, parse_bool_param(Value)}];
-parse_view_param("reduce", Value) ->
- [{reduce, parse_bool_param(Value)}];
-parse_view_param("include_docs", Value) ->
- [{include_docs, parse_bool_param(Value)}];
-parse_view_param("list", Value) ->
- [{list, ?l2b(Value)}];
-parse_view_param("callback", _) ->
- []; % Verified in the JSON response functions
-parse_view_param("show_total_rows", Value) ->
- [{show_total_rows, parse_bool_param(Value)}];
-parse_view_param("sorted", Value) ->
- [{sorted, parse_bool_param(Value)}];
-parse_view_param(Key, Value) ->
- [{extra, {Key, Value}}].
-
-validate_view_query(start_key, Value, Args) ->
- case Args#view_query_args.multi_get of
- true ->
- Msg = <<"Query parameter `start_key` is "
- "not compatiible with multi-get">>,
- throw({query_parse_error, Msg});
- _ ->
- Args#view_query_args{start_key=Value}
- end;
-validate_view_query(start_docid, Value, Args) ->
- Args#view_query_args{start_docid=Value};
-validate_view_query(end_key, Value, Args) ->
- case Args#view_query_args.multi_get of
- true->
- Msg = <<"Query paramter `end_key` is "
- "not compatibile with multi-get">>,
- throw({query_parse_error, Msg});
- _ ->
- Args#view_query_args{end_key=Value}
- end;
-validate_view_query(end_docid, Value, Args) ->
- Args#view_query_args{end_docid=Value};
-validate_view_query(limit, Value, Args) ->
- Args#view_query_args{limit=Value};
-validate_view_query(list, Value, Args) ->
- Args#view_query_args{list=Value};
-validate_view_query(stale, Value, Args) ->
- Args#view_query_args{stale=Value};
-validate_view_query(descending, true, Args) ->
- case Args#view_query_args.direction of
- rev -> Args; % Already reversed
- fwd ->
- Args#view_query_args{
- direction = rev,
- start_docid =
- reverse_key_default(Args#view_query_args.start_docid),
- end_docid =
- reverse_key_default(Args#view_query_args.end_docid)
- }
- end;
-validate_view_query(descending, false, Args) ->
- Args; % Ignore default condition
-validate_view_query(skip, Value, Args) ->
- Args#view_query_args{skip=Value};
-validate_view_query(group_level, Value, Args) ->
- case Args#view_query_args.view_type of
- reduce ->
- Args#view_query_args{group_level=Value};
- _ ->
- Msg = <<"Invalid URL parameter 'group' or "
- " 'group_level' for non-reduce view.">>,
- throw({query_parse_error, Msg})
- end;
-validate_view_query(inclusive_end, Value, Args) ->
- Args#view_query_args{inclusive_end=Value};
-validate_view_query(reduce, _, Args) ->
- case Args#view_query_args.view_type of
- map ->
- Msg = <<"Invalid URL parameter `reduce` for map view.">>,
- throw({query_parse_error, Msg});
- _ ->
- Args
- end;
-validate_view_query(include_docs, true, Args) ->
- case Args#view_query_args.view_type of
- reduce ->
- Msg = <<"Query paramter `include_docs` "
- "is invalid for reduce views.">>,
- throw({query_parse_error, Msg});
- _ ->
- Args#view_query_args{include_docs=true}
- end;
-validate_view_query(include_docs, _Value, Args) ->
- Args;
-validate_view_query(sorted, false, Args) ->
- Args#view_query_args{sorted=false};
-validate_view_query(sorted, _Value, Args) ->
- Args;
-validate_view_query(extra, _Value, Args) ->
- Args.
-
-view_group_etag(Group, Db) ->
- view_group_etag(Group, Db, nil).
-
-view_group_etag(#group{sig=Sig,current_seq=CurrentSeq}, _Db, Extra) ->
- % ?LOG_ERROR("Group ~p",[Group]),
- % This is not as granular as it could be.
- % If there are updates to the db that do not effect the view index,
- % they will change the Etag. For more granular Etags we'd need to keep
- % track of the last Db seq that caused an index change.
- chttpd:make_etag({Sig, CurrentSeq, Extra}).
-
-parse_bool_param("true") -> true;
-parse_bool_param("false") -> false;
-parse_bool_param(Val) ->
- Msg = io_lib:format("Invalid value for boolean paramter: ~p", [Val]),
- throw({query_parse_error, ?l2b(Msg)}).
-
-parse_int_param(Val) ->
- case (catch list_to_integer(Val)) of
- IntVal when is_integer(IntVal) ->
- IntVal;
- _ ->
- Msg = io_lib:format("Invalid value for integer parameter: ~p", [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
-
-parse_positive_int_param(Val) ->
- case parse_int_param(Val) of
- IntVal when IntVal >= 0 ->
- IntVal;
- _ ->
- Fmt = "Invalid value for positive integer parameter: ~p",
- Msg = io_lib:format(Fmt, [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.