feat(queue): add stub for CRUD API
This commit is contained in:
parent
23f0e88b45
commit
303ff95e10
|
@ -0,0 +1,218 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2022-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_ds_shared_sub_api).
|
||||
|
||||
-behaviour(minirest_api).
|
||||
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
-include_lib("hocon/include/hoconsc.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
|
||||
%% Swagger specs from hocon schema
|
||||
-export([
|
||||
api_spec/0,
|
||||
paths/0,
|
||||
schema/1,
|
||||
namespace/0
|
||||
]).
|
||||
|
||||
-export([
|
||||
fields/1,
|
||||
roots/0
|
||||
]).
|
||||
|
||||
-define(TAGS, [<<"Durable Queues">>]).
|
||||
|
||||
%% API callbacks
|
||||
-export([
|
||||
'/durable_queues'/2,
|
||||
'/durable_queues/:id'/2
|
||||
]).
|
||||
|
||||
-import(hoconsc, [mk/2, ref/1, ref/2]).
|
||||
-import(emqx_dashboard_swagger, [error_codes/2]).
|
||||
|
||||
namespace() -> "durable_queues".
|
||||
|
||||
api_spec() ->
|
||||
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
|
||||
|
||||
paths() ->
|
||||
[
|
||||
"/durable_queues",
|
||||
"/durable_queues/:id"
|
||||
].
|
||||
|
||||
-define(NOT_FOUND, 'NOT_FOUND').
|
||||
|
||||
schema("/durable_queues") ->
|
||||
#{
|
||||
'operationId' => '/durable_queues',
|
||||
get => #{
|
||||
tags => ?TAGS,
|
||||
summary => <<"List declared durable queues">>,
|
||||
description => ?DESC("durable_queues_get"),
|
||||
responses => #{
|
||||
200 => emqx_dashboard_swagger:schema_with_example(
|
||||
durable_queues_get(),
|
||||
durable_queues_get_example()
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
schema("/durable_queues/:id") ->
|
||||
#{
|
||||
'operationId' => '/durable_queues/:id',
|
||||
get => #{
|
||||
tags => ?TAGS,
|
||||
summary => <<"Get a declared durable queue">>,
|
||||
description => ?DESC("durable_queue_get"),
|
||||
parameters => [param_queue_id()],
|
||||
responses => #{
|
||||
200 => emqx_dashboard_swagger:schema_with_example(
|
||||
durable_queue_get(),
|
||||
durable_queue_get_example()
|
||||
),
|
||||
404 => error_codes([?NOT_FOUND], <<"Queue Not Found">>)
|
||||
}
|
||||
},
|
||||
delete => #{
|
||||
tags => ?TAGS,
|
||||
summary => <<"Delete a declared durable queue">>,
|
||||
description => ?DESC("durable_queue_delete"),
|
||||
parameters => [param_queue_id()],
|
||||
responses => #{
|
||||
200 => <<"Queue deleted">>,
|
||||
404 => error_codes([?NOT_FOUND], <<"Queue Not Found">>)
|
||||
}
|
||||
},
|
||||
put => #{
|
||||
tags => ?TAGS,
|
||||
summary => <<"Declare a durable queue">>,
|
||||
description => ?DESC("durable_queues_put"),
|
||||
parameters => [param_queue_id()],
|
||||
'requestBody' => durable_queue_put(),
|
||||
responses => #{
|
||||
200 => emqx_dashboard_swagger:schema_with_example(
|
||||
durable_queue_get(),
|
||||
durable_queue_get_example()
|
||||
)
|
||||
}
|
||||
}
|
||||
}.
|
||||
|
||||
'/durable_queues'(get, _Params) ->
|
||||
{200, queue_list()}.
|
||||
|
||||
'/durable_queues/:id'(get, Params) ->
|
||||
case queue_get(Params) of
|
||||
{ok, Queue} -> {200, Queue};
|
||||
not_found -> serialize_error(not_found)
|
||||
end;
|
||||
'/durable_queues/:id'(delete, Params) ->
|
||||
case queue_delete(Params) of
|
||||
ok -> {200, <<"Queue deleted">>};
|
||||
not_found -> serialize_error(not_found)
|
||||
end;
|
||||
'/durable_queues/:id'(put, Params) ->
|
||||
{200, queue_put(Params)}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Actual handlers: stubs
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
queue_list() ->
|
||||
persistent_term:get({?MODULE, queues}, []).
|
||||
|
||||
queue_get(#{bindings := #{id := ReqId}}) ->
|
||||
case [Q || #{id := Id} = Q <- queue_list(), Id =:= ReqId] of
|
||||
[Queue] -> {ok, Queue};
|
||||
[] -> not_found
|
||||
end.
|
||||
|
||||
queue_delete(#{bindings := #{id := ReqId}}) ->
|
||||
Queues0 = queue_list(),
|
||||
Queues1 = [Q || #{id := Id} = Q <- Queues0, Id =/= ReqId],
|
||||
persistent_term:put({?MODULE, queues}, Queues1),
|
||||
case Queues0 =:= Queues1 of
|
||||
true -> not_found;
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
queue_put(#{bindings := #{id := ReqId}}) ->
|
||||
Queues0 = queue_list(),
|
||||
Queues1 = [Q || #{id := Id} = Q <- Queues0, Id =/= ReqId],
|
||||
NewQueue = #{
|
||||
id => ReqId
|
||||
},
|
||||
Queues2 = [NewQueue | Queues1],
|
||||
persistent_term:put({?MODULE, queues}, Queues2),
|
||||
NewQueue.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Schemas
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
param_queue_id() ->
|
||||
{
|
||||
id,
|
||||
mk(binary(), #{
|
||||
in => path,
|
||||
desc => ?DESC(param_queue_id),
|
||||
required => true,
|
||||
validator => fun validate_queue_id/1
|
||||
})
|
||||
}.
|
||||
|
||||
validate_queue_id(Id) ->
|
||||
case emqx_topic:words(Id) of
|
||||
[Segment] when is_binary(Segment) -> true;
|
||||
_ -> {error, <<"Invalid queue id">>}
|
||||
end.
|
||||
|
||||
durable_queues_get() ->
|
||||
hoconsc:array(ref(durable_queue_get)).
|
||||
|
||||
durable_queue_get() ->
|
||||
ref(durable_queue_get).
|
||||
|
||||
durable_queue_put() ->
|
||||
map().
|
||||
|
||||
roots() -> [].
|
||||
|
||||
fields(durable_queue_get) ->
|
||||
[
|
||||
{id, mk(binary(), #{})}
|
||||
].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Examples
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
durable_queue_get_example() ->
|
||||
#{
|
||||
id => <<"queue1">>
|
||||
}.
|
||||
|
||||
durable_queues_get_example() ->
|
||||
[
|
||||
#{
|
||||
id => <<"queue1">>
|
||||
},
|
||||
#{
|
||||
id => <<"queue2">>
|
||||
}
|
||||
].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Error codes
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
serialize_error(not_found) ->
|
||||
{404, #{
|
||||
code => <<"NOT_FOUND">>,
|
||||
message => <<"Queue Not Found">>
|
||||
}}.
|
|
@ -0,0 +1,140 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2022-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_ds_shared_sub_api_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-import(
|
||||
emqx_mgmt_api_test_util,
|
||||
[
|
||||
request_api/2,
|
||||
request/3,
|
||||
uri/1
|
||||
]
|
||||
).
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
||||
init_per_suite(Config) ->
|
||||
Apps = emqx_cth_suite:start(
|
||||
[
|
||||
{emqx, #{
|
||||
config => #{
|
||||
<<"durable_sessions">> => #{
|
||||
<<"enable">> => true,
|
||||
<<"renew_streams_interval">> => "100ms"
|
||||
},
|
||||
<<"durable_storage">> => #{
|
||||
<<"messages">> => #{
|
||||
<<"backend">> => <<"builtin_raft">>
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
emqx_ds_shared_sub,
|
||||
emqx_management,
|
||||
emqx_mgmt_api_test_util:emqx_dashboard()
|
||||
],
|
||||
#{work_dir => ?config(priv_dir, Config)}
|
||||
),
|
||||
[{apps, Apps} | Config].
|
||||
|
||||
end_per_suite(Config) ->
|
||||
ok = emqx_cth_suite:stop(?config(apps, Config)),
|
||||
ok.
|
||||
|
||||
init_per_testcase(_TC, Config) ->
|
||||
ok = snabbkaffe:start_trace(),
|
||||
Config.
|
||||
|
||||
end_per_testcase(_TC, _Config) ->
|
||||
ok = snabbkaffe:stop(),
|
||||
ok = terminate_leaders(),
|
||||
ok.
|
||||
%%--------------------------------------------------------------------
|
||||
%% Tests
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
t_basic_crud(_Config) ->
|
||||
?assertMatch(
|
||||
{ok, []},
|
||||
api_get(["durable_queues"])
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, 200, #{
|
||||
<<"id">> := <<"q1">>
|
||||
}},
|
||||
api(put, ["durable_queues", "q1"], #{})
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{error, {_, 404, _}},
|
||||
api_get(["durable_queues", "q2"])
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, 200, #{
|
||||
<<"id">> := <<"q2">>
|
||||
}},
|
||||
api(put, ["durable_queues", "q2"], #{})
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, #{
|
||||
<<"id">> := <<"q2">>
|
||||
}},
|
||||
api_get(["durable_queues", "q2"])
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, [#{<<"id">> := <<"q2">>}, #{<<"id">> := <<"q1">>}]},
|
||||
api_get(["durable_queues"])
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, 200, <<"Queue deleted">>},
|
||||
api(delete, ["durable_queues", "q2"], #{})
|
||||
),
|
||||
|
||||
?assertMatch(
|
||||
{ok, [#{<<"id">> := <<"q1">>}]},
|
||||
api_get(["durable_queues"])
|
||||
).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Helpers
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
api_get(Path) ->
|
||||
case request_api(get, uri(Path)) of
|
||||
{ok, ResponseBody} ->
|
||||
{ok, jiffy:decode(list_to_binary(ResponseBody), [return_maps])};
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
api(Method, Path, Data) ->
|
||||
case request(Method, uri(Path), Data) of
|
||||
{ok, Code, ResponseBody} ->
|
||||
Res =
|
||||
case emqx_utils_json:safe_decode(ResponseBody, [return_maps]) of
|
||||
{ok, Decoded} -> Decoded;
|
||||
{error, _} -> ResponseBody
|
||||
end,
|
||||
{ok, Code, Res};
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
terminate_leaders() ->
|
||||
ok = supervisor:terminate_child(emqx_ds_shared_sub_sup, emqx_ds_shared_sub_leader_sup),
|
||||
{ok, _} = supervisor:restart_child(emqx_ds_shared_sub_sup, emqx_ds_shared_sub_leader_sup),
|
||||
ok.
|
|
@ -0,0 +1,34 @@
|
|||
emqx_ds_shared_sub_api {
|
||||
|
||||
param_queue_id.desc:
|
||||
"""The ID of the durable queue."""
|
||||
|
||||
param_queue_id.label:
|
||||
"""Queue ID"""
|
||||
|
||||
durable_queues_get.desc:
|
||||
"""Get the list of durable queues."""
|
||||
|
||||
durable_queues_get.label:
|
||||
"""Durable Queues"""
|
||||
|
||||
durable_queue_get.desc:
|
||||
"""Get the information of a durable queue."""
|
||||
|
||||
durable_queue_get.label:
|
||||
"""Durable Queue"""
|
||||
|
||||
durable_queue_delete.desc:
|
||||
"""Delete a durable queue."""
|
||||
|
||||
durable_queue_delete.label:
|
||||
"""Delete Durable Queue"""
|
||||
|
||||
durable_queues_put.desc:
|
||||
"""Create a durable queue."""
|
||||
|
||||
durable_queues_put.label:
|
||||
"""Create Durable Queue"""
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue