summaryrefslogtreecommitdiff
path: root/src/chttpd/src/chttpd_show.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/chttpd/src/chttpd_show.erl')
-rw-r--r--src/chttpd/src/chttpd_show.erl331
1 files changed, 0 insertions, 331 deletions
diff --git a/src/chttpd/src/chttpd_show.erl b/src/chttpd/src/chttpd_show.erl
deleted file mode 100644
index e798a98d6..000000000
--- a/src/chttpd/src/chttpd_show.erl
+++ /dev/null
@@ -1,331 +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_show).
-
--export([handle_doc_show_req/3, handle_doc_update_req/3, handle_view_list_req/3]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
-% /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, Options) ->
- case fabric:open_doc(Db, DocId, Options) of
- {ok, Doc} ->
- chttpd_stats:incr_reads(),
- Doc;
- {not_found, _} ->
- nil
- end.
-
-handle_doc_show_req(
- #httpd{
- path_parts = [_, _, _, _, ShowName, DocId]
- } = Req,
- Db,
- DDoc
-) ->
- % open the doc
- Options = [conflicts, {user_ctx, Req#httpd.user_ctx}],
- Doc = maybe_open_doc(Db, DocId, Options),
-
- % 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 = [_, _, _, _, ShowName, DocId | Rest]
- } = Req,
- Db,
- DDoc
-) ->
- DocParts = [DocId | Rest],
- DocId1 = ?l2b(string:join([?b2l(P) || P <- DocParts], "/")),
-
- % open the doc
- Options = [conflicts, {user_ctx, Req#httpd.user_ctx}],
- Doc = maybe_open_doc(Db, DocId1, Options),
-
- % 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) ->
- chttpd: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) ->
- %% Will throw an exception if the _show handler is missing
- couch_util:get_nested_json_value(DDoc#doc.body, [<<"shows">>, ShowName]),
- % 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
- }).
-
-% /db/_design/foo/update/bar/docid
-% updates a doc based on a request
-% handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db, _DDoc) ->
-% % anything but GET
-% send_method_not_allowed(Req, "POST,PUT,DELETE,ETC");
-
-handle_doc_update_req(
- #httpd{
- path_parts = [_, _, _, _, UpdateName]
- } = Req,
- Db,
- DDoc
-) ->
- send_doc_update_response(Req, Db, DDoc, UpdateName, nil, null);
-handle_doc_update_req(
- #httpd{
- path_parts = [_, _, _, _, UpdateName | DocIdParts]
- } = Req,
- Db,
- DDoc
-) ->
- DocId = ?l2b(string:join([?b2l(P) || P <- DocIdParts], "/")),
- Options = [conflicts, {user_ctx, Req#httpd.user_ctx}],
- Doc = maybe_open_doc(Db, DocId, Options),
- send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId);
-handle_doc_update_req(Req, _Db, _DDoc) ->
- chttpd:send_error(Req, 404, <<"update_error">>, <<"Invalid path.">>).
-
-send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) ->
- %% Will throw an exception if the _update handler is missing
- couch_util:get_nested_json_value(DDoc#doc.body, [<<"updates">>, UpdateName]),
- JsonReq = chttpd_external:json_req_obj(Req, Db, DocId),
- JsonDoc = couch_query_servers:json_doc(Doc),
- Cmd = [<<"updates">>, UpdateName],
- W = chttpd:qs_value(Req, "w", integer_to_list(mem3:quorum(Db))),
- UpdateResp = couch_query_servers:ddoc_prompt(DDoc, Cmd, [JsonDoc, JsonReq]),
- JsonResp =
- case UpdateResp of
- [<<"up">>, {NewJsonDoc}, {JsonResp0}] ->
- case chttpd:header_value(Req, "X-Couch-Full-Commit", "false") of
- "true" ->
- Options = [full_commit, {user_ctx, Req#httpd.user_ctx}, {w, W}];
- _ ->
- Options = [{user_ctx, Req#httpd.user_ctx}, {w, W}]
- end,
- NewDoc = couch_db:doc_from_json_obj_validate(Db, {NewJsonDoc}),
- couch_doc:validate_docid(NewDoc#doc.id),
- {UpdateResult, NewRev} = fabric:update_doc(Db, NewDoc, Options),
- chttpd_stats:incr_writes(),
- NewRevStr = couch_doc:rev_to_str(NewRev),
- case {UpdateResult, NewRev} of
- {ok, _} ->
- Code = 201;
- {accepted, _} ->
- Code = 202
- end,
- {JsonResp1} = apply_headers(JsonResp0, [
- {<<"X-Couch-Update-NewRev">>, NewRevStr},
- {<<"X-Couch-Id">>, couch_util:url_encode(NewDoc#doc.id)}
- ]),
- {[{<<"code">>, Code} | JsonResp1]};
- [<<"up">>, _Other, {JsonResp0}] ->
- {[{<<"code">>, 200} | JsonResp0]}
- end,
- % todo set location field
- chttpd_external:send_external_response(Req, JsonResp).
-
-% view-list request with view and list from same design doc.
-handle_view_list_req(
- #httpd{
- method = Method,
- path_parts = [_, _, DesignName, _, ListName, ViewName]
- } = Req,
- Db,
- DDoc
-) when
- Method =:= 'GET' orelse Method =:= 'OPTIONS'
-->
- Keys = chttpd:qs_json_value(Req, "keys", undefined),
- handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys);
-% view-list request with view and list from different design docs.
-handle_view_list_req(
- #httpd{
- method = Method,
- path_parts = [_, _, _, _, ListName, DesignName, ViewName]
- } = Req,
- Db,
- DDoc
-) when
- Method =:= 'GET' orelse Method =:= 'OPTIONS'
-->
- Keys = chttpd:qs_json_value(Req, "keys", undefined),
- handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys);
-handle_view_list_req(#httpd{method = Method} = Req, _Db, _DDoc) when
- Method =:= 'GET' orelse Method =:= 'OPTIONS'
-->
- chttpd:send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>);
-handle_view_list_req(
- #httpd{
- method = 'POST',
- path_parts = [_, _, DesignName, _, ListName, ViewName]
- } = Req,
- Db,
- DDoc
-) ->
- chttpd:validate_ctype(Req, "application/json"),
- {Props} = chttpd:json_body(Req),
- Keys = proplists:get_value(<<"keys">>, Props, undefined),
- handle_view_list(
- Req#httpd{req_body = {Props}},
- Db,
- DDoc,
- ListName,
- {DesignName, ViewName},
- Keys
- );
-handle_view_list_req(
- #httpd{
- method = 'POST',
- path_parts = [_, _, _, _, ListName, DesignName, ViewName]
- } = Req,
- Db,
- DDoc
-) ->
- chttpd:validate_ctype(Req, "application/json"),
- {Props} = chttpd:json_body(Req),
- Keys = proplists:get_value(<<"keys">>, Props, undefined),
- handle_view_list(
- Req#httpd{req_body = {Props}},
- Db,
- DDoc,
- ListName,
- {DesignName, ViewName},
- Keys
- );
-handle_view_list_req(#httpd{method = 'POST'} = Req, _Db, _DDoc) ->
- chttpd:send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>);
-handle_view_list_req(Req, _Db, _DDoc) ->
- chttpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-handle_view_list(Req, Db, DDoc, LName, {ViewDesignName, ViewName}, Keys) ->
- %% Will throw an exception if the _list handler is missing
- couch_util:get_nested_json_value(DDoc#doc.body, [<<"lists">>, LName]),
- DbName = couch_db:name(Db),
- {ok, VDoc} = ddoc_cache:open(DbName, <<"_design/", ViewDesignName/binary>>),
- CB = fun list_cb/2,
- QueryArgs = couch_mrview_http:parse_body_and_query(Req, Keys),
- Options = [{user_ctx, Req#httpd.user_ctx}],
- couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) ->
- Acc = #lacc{
- lname = LName,
- req = Req,
- qserver = QServer,
- db = Db
- },
- case ViewName of
- <<"_all_docs">> ->
- fabric:all_docs(Db, Options, CB, Acc, QueryArgs);
- _ ->
- fabric:query_view(
- Db,
- Options,
- VDoc,
- ViewName,
- CB,
- Acc,
- QueryArgs
- )
- end
- end).
-
-list_cb({row, Row} = Msg, Acc) ->
- case lists:keymember(doc, 1, Row) of
- true -> chttpd_stats:incr_reads();
- false -> ok
- end,
- chttpd_stats:incr_rows(),
- couch_mrview_show:list_cb(Msg, Acc);
-list_cb(Msg, Acc) ->
- couch_mrview_show:list_cb(Msg, Acc).
-
-% 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
- json_apply_field({Key, NewValue}, Headers, Acc);
-json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
- % something else is next, leave it alone.
- json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
-json_apply_field({Key, NewValue}, [], Acc) ->
- % end of list, add ours
- {[{Key, NewValue} | Acc]}.
-
-apply_etag(JsonResp, undefined) ->
- JsonResp;
-apply_etag({ExternalResponse}, CurrentEtag) ->
- % Here we embark on the delicate task of replacing or creating the
- % headers on the JsonResponse object. We need to control the Etag and
- % Vary headers. If the external function controls the Etag, we'd have to
- % run it to check for a match, which sort of defeats the purpose.
- apply_headers(ExternalResponse, [
- {<<"ETag">>, CurrentEtag},
- {<<"Vary">>, <<"Accept">>}
- ]).
-
-apply_headers(JsonResp, []) ->
- JsonResp;
-apply_headers(JsonResp, NewHeaders) ->
- case couch_util:get_value(<<"headers">>, JsonResp) of
- undefined ->
- {[{<<"headers">>, {NewHeaders}} | JsonResp]};
- JsonHeaders ->
- Headers = apply_headers1(JsonHeaders, NewHeaders),
- NewKV = {<<"headers">>, Headers},
- {lists:keyreplace(<<"headers">>, 1, JsonResp, NewKV)}
- end.
-apply_headers1(JsonHeaders, [{Key, Value} | Rest]) ->
- NewJsonHeaders = json_apply_field({Key, Value}, JsonHeaders),
- apply_headers1(NewJsonHeaders, Rest);
-apply_headers1(JsonHeaders, []) ->
- JsonHeaders.