Skip to content

Commit 9dffb8f

Browse files
jiahuili430nickva
authored andcommitted
add GET _dbs_info
1 parent 86facac commit 9dffb8f

File tree

4 files changed

+194
-28
lines changed

4 files changed

+194
-28
lines changed

src/chttpd/src/chttpd_misc.erl

+30-9
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ handle_utils_dir_req(Req, _) ->
115115
send_method_not_allowed(Req, "GET,HEAD").
116116

117117
handle_all_dbs_req(#httpd{method = 'GET'} = Req) ->
118+
handle_all_dbs_info_req(Req);
119+
handle_all_dbs_req(Req) ->
120+
send_method_not_allowed(Req, "GET,HEAD").
121+
122+
handle_all_dbs_info_req(Req) ->
118123
Args = couch_mrview_http:parse_params(Req, undefined),
119124
ShardDbName = config:get("mem3", "shards_db", "_dbs"),
120125
%% 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) ->
125130
{ok, Resp} = chttpd:etag_respond(Req, Etag, fun() ->
126131
{ok, Resp} = chttpd:start_delayed_json_response(Req, 200, [{"ETag", Etag}]),
127132
VAcc = #vacc{req = Req, resp = Resp},
128-
fabric:all_docs(ShardDbName, Options, fun all_dbs_callback/2, VAcc, Args)
133+
fabric:all_docs(ShardDbName, Options, fun all_dbs_info_callback/2, VAcc, Args)
129134
end),
130135
case is_record(Resp, vacc) of
131136
true -> {ok, Resp#vacc.resp};
132137
_ -> {ok, Resp}
133-
end;
134-
handle_all_dbs_req(Req) ->
135-
send_method_not_allowed(Req, "GET,HEAD").
138+
end.
136139

137-
all_dbs_callback({meta, _Meta}, #vacc{resp = Resp0} = Acc) ->
140+
all_dbs_info_callback({meta, _Meta}, #vacc{resp = Resp0} = Acc) ->
138141
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, "["),
139142
{ok, Acc#vacc{resp = Resp1}};
140-
all_dbs_callback({row, Row}, #vacc{resp = Resp0} = Acc) ->
143+
all_dbs_info_callback({row, Row}, #vacc{resp = Resp0} = Acc)
144+
when Acc#vacc.req#httpd.path_parts =:= [<<"_all_dbs">>] ->
141145
Prepend = couch_mrview_http:prepend_val(Acc),
142146
case couch_util:get_value(id, Row) of
143147
<<"_design", _/binary>> ->
@@ -146,14 +150,31 @@ all_dbs_callback({row, Row}, #vacc{resp = Resp0} = Acc) ->
146150
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, [Prepend, ?JSON_ENCODE(DbName)]),
147151
{ok, Acc#vacc{prepend = ",", resp = Resp1}}
148152
end;
149-
all_dbs_callback(complete, #vacc{resp = Resp0} = Acc) ->
153+
all_dbs_info_callback({row, Row}, #vacc{resp = Resp0} = Acc)
154+
when Acc#vacc.req#httpd.path_parts =:= [<<"_dbs_info">>] ->
155+
Prepend = couch_mrview_http:prepend_val(Acc),
156+
DbName = couch_util:get_value(id, Row),
157+
case chttpd_util:get_db_info(DbName) of
158+
{ok, DbInfo} ->
159+
Chunk = [Prepend, ?JSON_ENCODE({[{key, DbName}, {info, {DbInfo}}]})],
160+
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, Chunk),
161+
{ok, Acc#vacc{prepend = ",", resp = Resp1}};
162+
{error, database_does_not_exist} ->
163+
{ok, Acc#vacc{resp = Resp0}};
164+
{error, Reason} ->
165+
{ok, Resp1} = chttpd:send_delayed_error(Resp0, Reason),
166+
{stop, Acc#vacc{resp = Resp1}}
167+
end;
168+
all_dbs_info_callback(complete, #vacc{resp = Resp0} = Acc) ->
150169
{ok, Resp1} = chttpd:send_delayed_chunk(Resp0, "]"),
151170
{ok, Resp2} = chttpd:end_delayed_json_response(Resp1),
152171
{ok, Acc#vacc{resp = Resp2}};
153-
all_dbs_callback({error, Reason}, #vacc{resp = Resp0} = Acc) ->
172+
all_dbs_info_callback({error, Reason}, #vacc{resp = Resp0} = Acc) ->
154173
{ok, Resp1} = chttpd:send_delayed_error(Resp0, Reason),
155174
{ok, Acc#vacc{resp = Resp1}}.
156175

176+
handle_dbs_info_req(#httpd{method='GET'}=Req) ->
177+
handle_all_dbs_info_req(Req);
157178
handle_dbs_info_req(#httpd{method = 'POST'} = Req) ->
158179
chttpd:validate_ctype(Req, "application/json"),
159180
Props = chttpd:json_body_obj(Req),
@@ -192,7 +213,7 @@ handle_dbs_info_req(#httpd{method = 'POST'} = Req) ->
192213
send_chunk(Resp, "]"),
193214
chttpd:end_json_response(Resp);
194215
handle_dbs_info_req(Req) ->
195-
send_method_not_allowed(Req, "POST").
216+
send_method_not_allowed(Req, "GET,HEAD,POST").
196217

197218
handle_task_status_req(#httpd{method = 'GET'} = Req) ->
198219
ok = chttpd:verify_is_server_admin(Req),

src/chttpd/src/chttpd_util.erl

+11-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
get_chttpd_auth_config/2,
2222
get_chttpd_auth_config_integer/2,
2323
get_chttpd_auth_config_boolean/2,
24-
maybe_add_csp_header/3
24+
maybe_add_csp_header/3,
25+
get_db_info/1
2526
]).
2627

2728
get_chttpd_config(Key) ->
@@ -100,3 +101,12 @@ handle_legacy_config(OriginalHeaders, DefaultHeaderValue) ->
100101
false ->
101102
OriginalHeaders
102103
end.
104+
105+
get_db_info(DbName) ->
106+
Timeout = fabric_util:request_timeout(),
107+
IsolatedFun = fun() -> fabric:get_db_info(DbName) end,
108+
try
109+
fabric_util:isolate(IsolatedFun, Timeout)
110+
catch
111+
_Tag:Error -> {error, Error}
112+
end.

src/chttpd/test/eunit/chttpd_db_test.erl

+2-1
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,8 @@ should_not_change_db_proper_after_rewriting_shardmap(_) ->
411411

412412
{Prop2} = ?JSON_DECODE(?JSON_ENCODE({Props})),
413413
Shards2 = mem3_util:build_shards(TmpDb, Prop2),
414-
?assertEqual(Shards2, Shards)
414+
?assertEqual(Shards2, Shards),
415+
{ok, 200, _, _} = test_request:delete(BaseUrl, [?AUTH])
415416
end)}.
416417

417418
should_succeed_on_all_docs_with_queries_keys(Url) ->

src/chttpd/test/eunit/chttpd_dbs_info_test.erl

+151-17
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@ setup() ->
3030
create_db(Db1Url),
3131
Db2Url = lists:concat([Url, "db2"]),
3232
create_db(Db2Url),
33+
mock(fabric_util),
34+
mock(chttpd_util),
3335
Url.
3436

3537
teardown(Url) ->
38+
meck:unload(),
3639
Db1Url = lists:concat([Url, "db1"]),
3740
Db2Url = lists:concat([Url, "db2"]),
3841
delete_db(Db1Url),
@@ -46,6 +49,16 @@ create_db(Url) ->
4649
delete_db(Url) ->
4750
{ok, 200, _, _} = test_request:delete(Url, [?AUTH]).
4851

52+
mock(Module) ->
53+
meck:new(Module, [passthrough]).
54+
55+
mock_timeout() ->
56+
meck:expect(fabric_util, request_timeout, fun() -> 0 end).
57+
58+
mock_db_not_exist() ->
59+
meck:expect(chttpd_util, get_db_info,
60+
fun(_) -> {error, database_does_not_exist} end).
61+
4962
dbs_info_test_() ->
5063
{
5164
"chttpd dbs info tests",
@@ -58,7 +71,18 @@ dbs_info_test_() ->
5871
fun setup/0,
5972
fun teardown/1,
6073
[
61-
fun should_return_error_for_get_db_info/1,
74+
fun get_db_info_should_return_db_info/1,
75+
fun get_db_info_should_return_error_when_db_not_exist/1,
76+
fun get_db_info_should_return_error_when_time_out/1,
77+
fun should_return_error_for_put_dbs_info/1,
78+
fun should_return_dbs_info_for_get_dbs_info/1,
79+
fun should_return_nothing_when_db_not_exist_for_get_dbs_info/1,
80+
fun should_return_500_time_out_when_time_is_not_enough_for_get_dbs_info/1,
81+
fun should_return_db2_for_get_dbs_info_with_descending/1,
82+
fun should_return_db1_for_get_dbs_info_with_limit_1/1,
83+
fun should_return_db2_for_get_dbs_info_with_skip_1/1,
84+
fun should_return_dbs_info_with_correct_start_end_key/1,
85+
fun should_return_empty_list_with_wrong_start_end_key/1,
6286
fun should_return_dbs_info_for_single_db/1,
6387
fun should_return_dbs_info_for_multiple_dbs/1,
6488
fun should_return_error_for_exceeded_keys/1,
@@ -69,22 +93,132 @@ dbs_info_test_() ->
6993
}
7094
}.
7195

72-
should_return_error_for_get_db_info(Url) ->
73-
?_test(begin
74-
{ok, Code, _, ResultBody} = test_request:get(
75-
Url ++ "/_dbs_info?" ++
76-
"keys=[\"db1\"]",
77-
[?CONTENT_JSON, ?AUTH]
78-
),
79-
{Body} = jiffy:decode(ResultBody),
80-
[
81-
?assertEqual(
82-
<<"method_not_allowed">>,
83-
couch_util:get_value(<<"error">>, Body)
84-
),
85-
?assertEqual(405, Code)
86-
]
87-
end).
96+
97+
get_db_info_should_return_db_info(_) ->
98+
DbInfo = fabric:get_db_info("db1"),
99+
?_assertEqual(DbInfo, chttpd_util:get_db_info("db1")).
100+
101+
102+
get_db_info_should_return_error_when_db_not_exist(_) ->
103+
?_assertEqual({error, database_does_not_exist},
104+
chttpd_util:get_db_info("db_not_exist")).
105+
106+
107+
get_db_info_should_return_error_when_time_out(_) ->
108+
?_test(
109+
begin
110+
mock_timeout(),
111+
?assertEqual({error, timeout}, chttpd_util:get_db_info("db1"))
112+
end).
113+
114+
115+
should_return_error_for_put_dbs_info(Url) ->
116+
?_test(
117+
begin
118+
{ok, Code, _, ResultBody} = test_request:put(Url
119+
++ "_dbs_info", [?CONTENT_JSON, ?AUTH], ""),
120+
{Body} = jiffy:decode(ResultBody),
121+
?assertEqual(405, Code),
122+
?assertEqual(<<"method_not_allowed">>,
123+
couch_util:get_value(<<"error">>, Body))
124+
end).
125+
126+
127+
should_return_dbs_info_for_get_dbs_info(Url) ->
128+
?_test(
129+
begin
130+
{ok, _, _, ResultBody} = test_request:get(Url
131+
++ "_dbs_info", [?CONTENT_JSON, ?AUTH]),
132+
BodyJson = jiffy:decode(ResultBody),
133+
{Db1Data} = lists:nth(1, BodyJson),
134+
{Db2Data} = lists:nth(2, BodyJson),
135+
?assertEqual(2, length(BodyJson)),
136+
?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, Db1Data)),
137+
?assertEqual(<<"db2">>, couch_util:get_value(<<"key">>, Db2Data))
138+
end).
139+
140+
141+
should_return_nothing_when_db_not_exist_for_get_dbs_info(Url) ->
142+
?_test(
143+
begin
144+
mock_db_not_exist(),
145+
{ok, Code, _, ResultBody} = test_request:get(Url
146+
++ "_dbs_info", [?CONTENT_JSON, ?AUTH]),
147+
BodyJson = jiffy:decode(ResultBody),
148+
?assertEqual(200, Code),
149+
?assertEqual([], BodyJson)
150+
end).
151+
152+
153+
should_return_500_time_out_when_time_is_not_enough_for_get_dbs_info(Url) ->
154+
?_test(
155+
begin
156+
mock_timeout(),
157+
{ok, Code, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
158+
++ "?buffer_response=true", [?CONTENT_JSON, ?AUTH]),
159+
{Body} = jiffy:decode(ResultBody),
160+
?assertEqual(500, Code),
161+
?assertEqual(<<"timeout">>, couch_util:get_value(<<"error">>, Body))
162+
end).
163+
164+
165+
should_return_db2_for_get_dbs_info_with_descending(Url) ->
166+
?_test(
167+
begin
168+
{ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
169+
++ "?descending=true", [?CONTENT_JSON, ?AUTH]),
170+
BodyJson = jiffy:decode(ResultBody),
171+
{Db1Data} = lists:nth(1, BodyJson),
172+
{Db2Data} = lists:nth(2, BodyJson),
173+
?assertEqual(2, length(BodyJson)),
174+
?assertEqual(<<"db2">>, couch_util:get_value(<<"key">>, Db1Data)),
175+
?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, Db2Data))
176+
end).
177+
178+
179+
should_return_db1_for_get_dbs_info_with_limit_1(Url) ->
180+
?_test(
181+
begin
182+
{ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
183+
++ "?limit=1", [?CONTENT_JSON, ?AUTH]),
184+
BodyJson = jiffy:decode(ResultBody),
185+
{DbData} = lists:nth(1, BodyJson),
186+
?assertEqual(1, length(BodyJson)),
187+
?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, DbData))
188+
end).
189+
190+
191+
should_return_db2_for_get_dbs_info_with_skip_1(Url) ->
192+
?_test(
193+
begin
194+
{ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
195+
++ "?skip=1", [?CONTENT_JSON, ?AUTH]),
196+
BodyJson = jiffy:decode(ResultBody),
197+
{DbData} = lists:nth(1, BodyJson),
198+
?assertEqual(1, length(BodyJson)),
199+
?assertEqual(<<"db2">>, couch_util:get_value(<<"key">>, DbData))
200+
end).
201+
202+
203+
should_return_dbs_info_with_correct_start_end_key(Url) ->
204+
?_test(
205+
begin
206+
{ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
207+
++ "?startkey=\"db1\"&endkey=\"db2\"", [?CONTENT_JSON, ?AUTH]),
208+
BodyJson = jiffy:decode(ResultBody),
209+
{DbData} = lists:nth(1, BodyJson),
210+
?assertEqual(2, length(BodyJson)),
211+
?assertEqual(<<"db1">>, couch_util:get_value(<<"key">>, DbData))
212+
end).
213+
214+
215+
should_return_empty_list_with_wrong_start_end_key(Url) ->
216+
?_test(
217+
begin
218+
{ok, _, _, ResultBody} = test_request:get(Url ++ "_dbs_info"
219+
++ "?startkey=\"db3\"&endkey=\"db4\"", [?CONTENT_JSON, ?AUTH]),
220+
?assertEqual([], jiffy:decode(ResultBody))
221+
end).
88222

89223
should_return_dbs_info_for_single_db(Url) ->
90224
?_test(begin

0 commit comments

Comments
 (0)