summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Newson <rnewson@apache.org>2021-12-08 09:25:22 +0000
committerGitHub <noreply@github.com>2021-12-08 09:25:22 +0000
commitc1cc808beb9bab0a575a959e7d8248784e9f2ddc (patch)
tree046a31252d2de7003a1ae4a837be1414f9c9d701
parentc8b6d80d29f12bf247a1f845cbf27e764b065e54 (diff)
parent9dffb8fd3f7258af6b24602b036fa48bb5ffe303 (diff)
downloadcouchdb-erlang_ls_improve.tar.gz
Merge branch '3.x' into erlang_ls_improveerlang_ls_improve
-rw-r--r--src/chttpd/src/chttpd_misc.erl39
-rw-r--r--src/chttpd/src/chttpd_util.erl12
-rw-r--r--src/chttpd/test/eunit/chttpd_db_test.erl3
-rw-r--r--src/chttpd/test/eunit/chttpd_dbs_info_test.erl168
4 files changed, 194 insertions, 28 deletions
diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl
index 6d119572d..ca1794cba 100644
--- a/src/chttpd/src/chttpd_misc.erl
+++ b/src/chttpd/src/chttpd_misc.erl
@@ -115,6 +115,11 @@ handle_utils_dir_req(Req, _) ->
send_method_not_allowed(Req, "GET,HEAD").
handle_all_dbs_req(#httpd{method = 'GET'} = Req) ->
+ handle_all_dbs_info_req(Req);
+handle_all_dbs_req(Req) ->
+ send_method_not_allowed(Req, "GET,HEAD").
+
+handle_all_dbs_info_req(Req) ->
Args = couch_mrview_http:parse_params(Req, undefined),
ShardDbName = config:get("mem3", "shards_db", "_dbs"),
%% shard_db is not sharded but mem3:shards treats it as an edge case
@@ -125,19 +130,18 @@ handle_all_dbs_req(#httpd{method = 'GET'} = Req) ->
{ok, Resp} = chttpd:etag_respond(Req, Etag, fun() ->
{ok, Resp} = chttpd:start_delayed_json_response(Req, 200, [{"ETag", Etag}]),
VAcc = #vacc{req = Req, resp = Resp},
- fabric:all_docs(ShardDbName, Options, fun all_dbs_callback/2, VAcc, Args)
+ fabric:all_docs(ShardDbName, Options, fun all_dbs_info_callback/2, VAcc, Args)
end),
case is_record(Resp, vacc) of
true -> {ok, Resp#vacc.resp};
_ -> {ok, Resp}
- end;
-handle_all_dbs_req(Req) ->
- send_method_not_allowed(Req, "GET,HEAD").
+ end.
-all_dbs_callback({meta, _Meta}, #vacc{resp = Resp0} = Acc) ->
+all_dbs_info_callback({meta, _Meta}, #vacc{resp = Resp0} = Acc) ->
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, "["),
{ok, Acc#vacc{resp = Resp1}};
-all_dbs_callback({row, Row}, #vacc{resp = Resp0} = Acc) ->
+all_dbs_info_callback({row, Row}, #vacc{resp = Resp0} = Acc)
+ when Acc#vacc.req#httpd.path_parts =:= [<<"_all_dbs">>] ->
Prepend = couch_mrview_http:prepend_val(Acc),
case couch_util:get_value(id, Row) of
<<"_design", _/binary>> ->
@@ -146,14 +150,31 @@ all_dbs_callback({row, Row}, #vacc{resp = Resp0} = Acc) ->
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, [Prepend, ?JSON_ENCODE(DbName)]),
{ok, Acc#vacc{prepend = ",", resp = Resp1}}
end;
-all_dbs_callback(complete, #vacc{resp = Resp0} = Acc) ->
+all_dbs_info_callback({row, Row}, #vacc{resp = Resp0} = Acc)
+ when Acc#vacc.req#httpd.path_parts =:= [<<"_dbs_info">>] ->
+ Prepend = couch_mrview_http:prepend_val(Acc),
+ DbName = couch_util:get_value(id, Row),
+ case chttpd_util:get_db_info(DbName) of
+ {ok, DbInfo} ->
+ Chunk = [Prepend, ?JSON_ENCODE({[{key, DbName}, {info, {DbInfo}}]})],
+ {ok, Resp1} = chttpd:send_delayed_chunk(Resp0, Chunk),
+ {ok, Acc#vacc{prepend = ",", resp = Resp1}};
+ {error, database_does_not_exist} ->
+ {ok, Acc#vacc{resp = Resp0}};
+ {error, Reason} ->
+ {ok, Resp1} = chttpd:send_delayed_error(Resp0, Reason),
+ {stop, Acc#vacc{resp = Resp1}}
+ end;
+all_dbs_info_callback(complete, #vacc{resp = Resp0} = Acc) ->
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, "]"),
{ok, Resp2} = chttpd:end_delayed_json_response(Resp1),
{ok, Acc#vacc{resp = Resp2}};
-all_dbs_callback({error, Reason}, #vacc{resp = Resp0} = Acc) ->
+all_dbs_info_callback({error, Reason}, #vacc{resp = Resp0} = Acc) ->
{ok, Resp1} = chttpd:send_delayed_error(Resp0, Reason),
{ok, Acc#vacc{resp = Resp1}}.
+handle_dbs_info_req(#httpd{method='GET'}=Req) ->
+ handle_all_dbs_info_req(Req);
handle_dbs_info_req(#httpd{method = 'POST'} = Req) ->
chttpd:validate_ctype(Req, "application/json"),
Props = chttpd:json_body_obj(Req),
@@ -192,7 +213,7 @@ handle_dbs_info_req(#httpd{method = 'POST'} = Req) ->
send_chunk(Resp, "]"),
chttpd:end_json_response(Resp);
handle_dbs_info_req(Req) ->
- send_method_not_allowed(Req, "POST").
+ send_method_not_allowed(Req, "GET,HEAD,POST").
handle_task_status_req(#httpd{method = 'GET'} = Req) ->
ok = chttpd:verify_is_server_admin(Req),
diff --git a/src/chttpd/src/chttpd_util.erl b/src/chttpd/src/chttpd_util.erl
index ca4ffc8a7..955beca57 100644
--- a/src/chttpd/src/chttpd_util.erl
+++ b/src/chttpd/src/chttpd_util.erl
@@ -21,7 +21,8 @@
get_chttpd_auth_config/2,
get_chttpd_auth_config_integer/2,
get_chttpd_auth_config_boolean/2,
- maybe_add_csp_header/3
+ maybe_add_csp_header/3,
+ get_db_info/1
]).
get_chttpd_config(Key) ->
@@ -100,3 +101,12 @@ handle_legacy_config(OriginalHeaders, DefaultHeaderValue) ->
false ->
OriginalHeaders
end.
+
+get_db_info(DbName) ->
+ Timeout = fabric_util:request_timeout(),
+ IsolatedFun = fun() -> fabric:get_db_info(DbName) end,
+ try
+ fabric_util:isolate(IsolatedFun, Timeout)
+ catch
+ _Tag:Error -> {error, Error}
+ end.
diff --git a/src/chttpd/test/eunit/chttpd_db_test.erl b/src/chttpd/test/eunit/chttpd_db_test.erl
index 07661733f..c76b31581 100644
--- a/src/chttpd/test/eunit/chttpd_db_test.erl
+++ b/src/chttpd/test/eunit/chttpd_db_test.erl
@@ -411,7 +411,8 @@ should_not_change_db_proper_after_rewriting_shardmap(_) ->
{Prop2} = ?JSON_DECODE(?JSON_ENCODE({Props})),
Shards2 = mem3_util:build_shards(TmpDb, Prop2),
- ?assertEqual(Shards2, Shards)
+ ?assertEqual(Shards2, Shards),
+ {ok, 200, _, _} = test_request:delete(BaseUrl, [?AUTH])
end)}.
should_succeed_on_all_docs_with_queries_keys(Url) ->
diff --git a/src/chttpd/test/eunit/chttpd_dbs_info_test.erl b/src/chttpd/test/eunit/chttpd_dbs_info_test.erl
index b4027eead..71ee86bc9 100644
--- a/src/chttpd/test/eunit/chttpd_dbs_info_test.erl
+++ b/src/chttpd/test/eunit/chttpd_dbs_info_test.erl
@@ -30,9 +30,12 @@ setup() ->
create_db(Db1Url),
Db2Url = lists:concat([Url, "db2"]),
create_db(Db2Url),
+ mock(fabric_util),
+ mock(chttpd_util),
Url.
teardown(Url) ->
+ meck:unload(),
Db1Url = lists:concat([Url, "db1"]),
Db2Url = lists:concat([Url, "db2"]),
delete_db(Db1Url),
@@ -46,6 +49,16 @@ create_db(Url) ->
delete_db(Url) ->
{ok, 200, _, _} = test_request:delete(Url, [?AUTH]).
+mock(Module) ->
+ meck:new(Module, [passthrough]).
+
+mock_timeout() ->
+ meck:expect(fabric_util, request_timeout, fun() -> 0 end).
+
+mock_db_not_exist() ->
+ meck:expect(chttpd_util, get_db_info,
+ fun(_) -> {error, database_does_not_exist} end).
+
dbs_info_test_() ->
{
"chttpd dbs info tests",
@@ -58,7 +71,18 @@ dbs_info_test_() ->
fun setup/0,
fun teardown/1,
[
- fun should_return_error_for_get_db_info/1,
+ fun get_db_info_should_return_db_info/1,
+ fun get_db_info_should_return_error_when_db_not_exist/1,
+ fun get_db_info_should_return_error_when_time_out/1,
+ fun should_return_error_for_put_dbs_info/1,
+ fun should_return_dbs_info_for_get_dbs_info/1,
+ fun should_return_nothing_when_db_not_exist_for_get_dbs_info/1,
+ fun should_return_500_time_out_when_time_is_not_enough_for_get_dbs_info/1,
+ fun should_return_db2_for_get_dbs_info_with_descending/1,
+ fun should_return_db1_for_get_dbs_info_with_limit_1/1,
+ fun should_return_db2_for_get_dbs_info_with_skip_1/1,
+ fun should_return_dbs_info_with_correct_start_end_key/1,
+ fun should_return_empty_list_with_wrong_start_end_key/1,
fun should_return_dbs_info_for_single_db/1,
fun should_return_dbs_info_for_multiple_dbs/1,
fun should_return_error_for_exceeded_keys/1,
@@ -69,22 +93,132 @@ dbs_info_test_() ->
}
}.
-should_return_error_for_get_db_info(Url) ->
- ?_test(begin
- {ok, Code, _, ResultBody} = test_request:get(
- Url ++ "/_dbs_info?" ++
- "keys=[\"db1\"]",
- [?CONTENT_JSON, ?AUTH]
- ),
- {Body} = jiffy:decode(ResultBody),
- [
- ?assertEqual(
- <<"method_not_allowed">>,
- couch_util:get_value(<<"error">>, Body)
- ),
- ?assertEqual(405, Code)
- ]
- end).
+
+get_db_info_should_return_db_info(_) ->
+ DbInfo = fabric:get_db_info("db1"),
+ ?_assertEqual(DbInfo, chttpd_util:get_db_info("db1")).
+
+
+get_db_info_should_return_error_when_db_not_exist(_) ->
+ ?_assertEqual({error, database_does_not_exist},
+ chttpd_util:get_db_info("db_not_exist")).
+
+
+get_db_info_should_return_error_when_time_out(_) ->
+ ?_test(
+ begin
+ mock_timeout(),
+ ?assertEqual({error, timeout}, chttpd_util:get_db_info("db1"))
+ end).
+
+
+should_return_error_for_put_dbs_info(Url) ->
+ ?_test(
+ begin
+ {ok, Code, _, ResultBody} = test_request:put(Url
+ ++ "_dbs_info", [?CONTENT_JSON, ?AUTH], ""),
+ {Body} = jiffy:decode(ResultBody),
+ ?assertEqual(405, Code),
+ ?assertEqual(<<"method_not_allowed">>,
+ couch_util:get_value(<<"error">>, Body))
+ end).
+
+
+should_return_dbs_info_for_get_dbs_info(Url) ->
+ ?_test(
+ begin
+ {ok, _, _, ResultBody} = test_request:get(Url
+ ++ "_dbs_info", [?CONTENT_JSON, ?AUTH]),
+ BodyJson = jiffy:decode(ResultBody),
+ {Db1Data} = lists:nth(1, BodyJson),
+ {Db2Data} = lists:nth(2, BodyJson),
+ ?assertEqual(2, length(BodyJson)),
+ ?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, Db1Data)),
+ ?assertEqual(<<"db2">>, couch_util:get_value(<<"key">>, Db2Data))
+ end).
+
+
+should_return_nothing_when_db_not_exist_for_get_dbs_info(Url) ->
+ ?_test(
+ begin
+ mock_db_not_exist(),
+ {ok, Code, _, ResultBody} = test_request:get(Url
+ ++ "_dbs_info", [?CONTENT_JSON, ?AUTH]),
+ BodyJson = jiffy:decode(ResultBody),
+ ?assertEqual(200, Code),
+ ?assertEqual([], BodyJson)
+ end).
+
+
+should_return_500_time_out_when_time_is_not_enough_for_get_dbs_info(Url) ->
+ ?_test(
+ begin
+ mock_timeout(),
+ {ok, Code, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
+ ++ "?buffer_response=true", [?CONTENT_JSON, ?AUTH]),
+ {Body} = jiffy:decode(ResultBody),
+ ?assertEqual(500, Code),
+ ?assertEqual(<<"timeout">>, couch_util:get_value(<<"error">>, Body))
+ end).
+
+
+should_return_db2_for_get_dbs_info_with_descending(Url) ->
+ ?_test(
+ begin
+ {ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
+ ++ "?descending=true", [?CONTENT_JSON, ?AUTH]),
+ BodyJson = jiffy:decode(ResultBody),
+ {Db1Data} = lists:nth(1, BodyJson),
+ {Db2Data} = lists:nth(2, BodyJson),
+ ?assertEqual(2, length(BodyJson)),
+ ?assertEqual(<<"db2">>, couch_util:get_value(<<"key">>, Db1Data)),
+ ?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, Db2Data))
+ end).
+
+
+should_return_db1_for_get_dbs_info_with_limit_1(Url) ->
+ ?_test(
+ begin
+ {ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
+ ++ "?limit=1", [?CONTENT_JSON, ?AUTH]),
+ BodyJson = jiffy:decode(ResultBody),
+ {DbData} = lists:nth(1, BodyJson),
+ ?assertEqual(1, length(BodyJson)),
+ ?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, DbData))
+ end).
+
+
+should_return_db2_for_get_dbs_info_with_skip_1(Url) ->
+ ?_test(
+ begin
+ {ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
+ ++ "?skip=1", [?CONTENT_JSON, ?AUTH]),
+ BodyJson = jiffy:decode(ResultBody),
+ {DbData} = lists:nth(1, BodyJson),
+ ?assertEqual(1, length(BodyJson)),
+ ?assertEqual(<<"db2">>, couch_util:get_value(<<"key">>, DbData))
+ end).
+
+
+should_return_dbs_info_with_correct_start_end_key(Url) ->
+ ?_test(
+ begin
+ {ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
+ ++ "?startkey=\"db1\"&endkey=\"db2\"", [?CONTENT_JSON, ?AUTH]),
+ BodyJson = jiffy:decode(ResultBody),
+ {DbData} = lists:nth(1, BodyJson),
+ ?assertEqual(2, length(BodyJson)),
+ ?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, DbData))
+ end).
+
+
+should_return_empty_list_with_wrong_start_end_key(Url) ->
+ ?_test(
+ begin
+ {ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
+ ++ "?startkey=\"db3\"&endkey=\"db4\"", [?CONTENT_JSON, ?AUTH]),
+ ?assertEqual([], jiffy:decode(ResultBody))
+ end).
should_return_dbs_info_for_single_db(Url) ->
?_test(begin