diff --git a/rebar.config b/rebar.config index bd28015b..23ed0791 100644 --- a/rebar.config +++ b/rebar.config @@ -1,6 +1,9 @@ %%-*- mode: erlang -*- -{erl_opts, [debug_info]}. +{erl_opts, [ + debug_info + ,{d, 'MAPS_SUPPORT'} +]}. {deps, [ {jsx, "2.8.0"}, diff --git a/src/couchbeam.erl b/src/couchbeam.erl index d349e846..96d3ee25 100644 --- a/src/couchbeam.erl +++ b/src/couchbeam.erl @@ -403,6 +403,22 @@ doc_exists(#db{server=Server, options=Opts}=Db, DocId) -> _Error -> false end. + +-ifdef('MAPS_SUPPORT'). +get_return_params(Params) -> + case proplists:get_value(return_maps, Params) == true of + true -> {[return_maps], proplists:delete(return_maps, Params)}; + false -> {[], Params} + end. +-else. +get_return_params(Params) -> + case proplists:get_value(return_maps, Params) == true of + true -> {[], proplists:delete(return_maps, Params)}; + false -> {[], Params} + end. +-endif. + + %% @doc open a document %% @equiv open_doc(Db, DocId, []) open_doc(Db, DocId) -> @@ -420,6 +436,10 @@ open_doc(#db{server=Server, options=Opts}=Db, DocId, Params) -> unefined -> {any, Params}; A -> {A, proplists:delete(accept, Params)} end, + + %% Set return format + {ReturnOptions, Params2} = get_return_params(Params1), + %% set the headers with the accepted content-type if needed Headers = case {Accept, proplists:get_value("attachments", Params)} of {any, true} -> @@ -432,8 +452,9 @@ open_doc(#db{server=Server, options=Opts}=Db, DocId, Params) -> _ -> [] end, + Url = hackney_url:make_url(couchbeam_httpc:server_url(Server), couchbeam_httpc:doc_url(Db, DocId1), - Params1), + Params2), case couchbeam_httpc:db_request(get, Url, Headers, <<>>, Opts, [200, 201]) of {ok, _, RespHeaders, Ref} -> @@ -445,7 +466,7 @@ open_doc(#db{server=Server, options=Opts}=Db, DocId, Params) -> end}, {ok, {multipart, InitialState}}; _ -> - {ok, couchbeam_httpc:json_body(Ref)} + {ok, couchbeam_httpc:json_body(Ref, ReturnOptions)} end; Error -> Error @@ -526,16 +547,56 @@ save_doc(Db, Doc, Options) -> %% `<<"identity">>' if normal or `<<"gzip">>' if the attachments is %% gzipped. --spec save_doc(Db::db(), doc(), mp_attachments(), Options::list()) -> - {ok, doc()} | {error, term()}. -save_doc(#db{server=Server, options=Opts}=Db, {Props}=Doc, Atts, Options) -> - DocId = case couchbeam_util:get_value(<<"_id">>, Props) of + +-ifdef('MAPS_SUPPORT'). +form_return_doc(Doc = #{}, NewRev, NewDocId) -> + Doc#{<<"_rev">> => NewRev, <<"_id">> => NewDocId}; +form_return_doc(Doc, NewRev, NewDocId) -> + couchbeam_doc:set_value(<<"_rev">>, NewRev, + couchbeam_doc:set_value(<<"_id">>, NewDocId, Doc)); +form_return_doc(Doc, NewRev, NewDocId) -> + couchbeam_doc:set_value(<<"_rev">>, NewRev, + couchbeam_doc:set_value(<<"_id">>, NewDocId, Doc)). +-else. +form_return_doc(Doc, NewRev, NewDocId) -> + couchbeam_doc:set_value(<<"_rev">>, NewRev, + couchbeam_doc:set_value(<<"_id">>, NewDocId, Doc)). +-endif. + + +% +-ifdef('MAPS_SUPPORT'). +get_doc_id(Doc = #{<<"_id">> := DocId1}, _Server) -> + couchbeam_util:encode_docid(DocId1); +get_doc_id(Doc = #{}, Server) -> + [Id] = get_uuid(Server), + Id; +get_doc_id({Props}, Server) -> + case couchbeam_util:get_value(<<"_id">>, Props) of undefined -> [Id] = get_uuid(Server), Id; DocId1 -> couchbeam_util:encode_docid(DocId1) - end, + end. +-else. +get_doc_id({Props}, Server) -> + case couchbeam_util:get_value(<<"_id">>, Props) of + undefined -> + [Id] = get_uuid(Server), + Id; + DocId1 -> + couchbeam_util:encode_docid(DocId1) + end. +-endif. + + + + +-spec save_doc(Db::db(), doc(), mp_attachments(), Options::list()) -> + {ok, doc()} | {error, term()}. +save_doc(#db{server=Server, options=Opts}=Db, Doc, Atts, Options) -> + DocId = get_doc_id(Doc, Server), Url = hackney_url:make_url(couchbeam_httpc:server_url(Server), couchbeam_httpc:doc_url(Db, DocId), Options), case Atts of @@ -548,8 +609,7 @@ save_doc(#db{server=Server, options=Opts}=Db, {Props}=Doc, Atts, Options) -> {JsonProp} = couchbeam_httpc:json_body(Ref), NewRev = couchbeam_util:get_value(<<"rev">>, JsonProp), NewDocId = couchbeam_util:get_value(<<"id">>, JsonProp), - Doc1 = couchbeam_doc:set_value(<<"_rev">>, NewRev, - couchbeam_doc:set_value(<<"_id">>, NewDocId, Doc)), + Doc1 = form_return_doc(Doc, NewRev, NewDocId), {ok, Doc1}; Error -> Error diff --git a/src/couchbeam_ejson.erl b/src/couchbeam_ejson.erl index 8bd02fcd..37844484 100644 --- a/src/couchbeam_ejson.erl +++ b/src/couchbeam_ejson.erl @@ -6,18 +6,18 @@ -module(couchbeam_ejson). --export([encode/1, decode/1]). +-export([encode/1, decode/1, decode/2]). -include("couchbeam.hrl"). -ifndef('WITH_JIFFY'). --define(JSON_ENCODE(D), jsx:encode(pre_encode(D))). --define(JSON_DECODE(D), post_decode(jsx:decode(D))). +-define(JSON_ENCODE(D), jsx:encode(map_or_pre_encode(D))). +-define(JSON_DECODE(D, DecodeOptions), map_or_post_decode(jsx:decode(D, DecodeOptions))). -else. -define(JSON_ENCODE(D), jiffy:encode(D, [uescape])). --define(JSON_DECODE(D), jiffy:decode(D)). +-define(JSON_DECODE(D, DecodeOptions), jiffy:decode(D, DecodeOptions)). -endif. @@ -31,9 +31,14 @@ encode(D) -> -spec decode(binary()) -> ejson(). %% @doc decode a binary to an EJSON term. Throw an exception if there is %% any error. -decode(D) -> +decode(D) -> decode(D, []). +decode(D, Options) -> + DecodeOptions = case proplists:get_value(return_maps, Options) == true of + true -> [return_maps]; + false -> [] + end, try - ?JSON_DECODE(D) + ?JSON_DECODE(D, DecodeOptions) catch throw:Error -> throw({invalid_json, Error}); @@ -41,6 +46,17 @@ decode(D) -> throw({invalid_json, badarg}) end. + +-ifdef('MAPS_SUPPORT'). +map_or_pre_encode(Map = #{}) -> + Map; +map_or_pre_encode(NotMap) -> + pre_encode(NotMap). +-else. +map_or_pre_encode(NotMap) -> + pre_encode(NotMap). +-endif. + pre_encode({[]}) -> [{}]; pre_encode({PropList}) -> @@ -60,6 +76,16 @@ pre_encode(Atom) when is_atom(Atom) -> pre_encode(Term) when is_integer(Term); is_float(Term); is_binary(Term) -> Term. +-ifdef('MAPS_SUPPORT'). +map_or_post_decode(Map = #{}) -> + Map; +map_or_post_decode(NotMap) -> + post_decode(NotMap). +-else. +map_or_post_decode(NotMap) -> + post_decode(NotMap). +-endif. + post_decode([{}]) -> {[]}; post_decode([{_Key, _Value} | _Rest] = PropList) -> diff --git a/src/couchbeam_httpc.erl b/src/couchbeam_httpc.erl index f12e276e..7a116181 100644 --- a/src/couchbeam_httpc.erl +++ b/src/couchbeam_httpc.erl @@ -7,7 +7,7 @@ -export([request/5, db_request/5, db_request/6, - json_body/1, + json_body/1, json_body/2, db_resp/2, make_headers/4, maybe_oauth_header/4]). @@ -31,9 +31,12 @@ db_request(Method, Url, Headers, Body, Options, Expect) -> Resp = request(Method, Url, Headers, Body, Options), db_resp(Resp, Expect). -json_body(Ref) -> + +json_body(Ref) -> + json_body(Ref, []). +json_body(Ref, Options) -> {ok, Body} = hackney:body(Ref), - couchbeam_ejson:decode(Body). + couchbeam_ejson:decode(Body, Options). make_headers(Method, Url, Headers, Options) -> Headers1 = case couchbeam_util:get_value(<<"Accept">>, Headers) of diff --git a/src/couchbeam_sup.erl b/src/couchbeam_sup.erl index 420c3b8f..cacb2858 100644 --- a/src/couchbeam_sup.erl +++ b/src/couchbeam_sup.erl @@ -31,4 +31,4 @@ init([]) -> {couchbeam_changes_sup, start_link, []}, permanent, 2000, supervisor, [couchbeam_changes_sup]}, - {ok, {{one_for_one, 10, 3600}, [Uuids, ViewSup, ChangesSup]}}. + {ok, {{one_for_one, 10, 10}, [Uuids, ViewSup, ChangesSup]}}. diff --git a/src/couchbeam_view_stream.erl b/src/couchbeam_view_stream.erl index 8d545e90..9a054174 100644 --- a/src/couchbeam_view_stream.erl +++ b/src/couchbeam_view_stream.erl @@ -42,7 +42,7 @@ async=false}). --define(TIMEOUT, 10000). +-define(TIMEOUT, 200000). -define(DEFAULT_CHANGES_POLL, 5000). % we check every 5secs @@ -382,7 +382,7 @@ maybe_continue_decoding(#viewst{parent=Parent, %% report the error report_error(Else, Ref, Owner), exit(Else) - after 5000 -> + after ?TIMEOUT -> erlang:hibernate(?MODULE, maybe_continue_decoding, [ViewSt]) end;