feat: create default personal calendar on email verification. Refs EventHub/EventHubBack#49
This commit is contained in:
@@ -19,7 +19,12 @@ init(Req, _Opts) ->
|
|||||||
{ok, UserId} ->
|
{ok, UserId} ->
|
||||||
core_user:update(UserId, [{status, active}]),
|
core_user:update(UserId, [{status, active}]),
|
||||||
core_verification:delete_token(Token),
|
core_verification:delete_token(Token),
|
||||||
handler_utils:send_json(Req1, 200, #{<<"message">> => <<"Account verified">>});
|
case logic_calendar:ensure_default_calendar(UserId) of
|
||||||
|
ok ->
|
||||||
|
handler_utils:send_json(Req1, 200, #{<<"message">> => <<"Account verified">>});
|
||||||
|
{error, Reason} ->
|
||||||
|
handler_utils:send_error(Req1, 500, Reason)
|
||||||
|
end;
|
||||||
{error, expired} ->
|
{error, expired} ->
|
||||||
handler_utils:send_error(Req1, 410, <<"Token expired">>);
|
handler_utils:send_error(Req1, 410, <<"Token expired">>);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
|
|
||||||
-export([create_calendar/3, create_calendar/4, get_calendar/2, list_calendars/1,
|
-export([create_calendar/3, create_calendar/4, get_calendar/2, list_calendars/1,
|
||||||
update_calendar/3, delete_calendar/2]).
|
update_calendar/3, delete_calendar/2, ensure_default_calendar/1]).
|
||||||
-export([can_access/2, can_edit/2]).
|
-export([can_access/2, can_edit/2]).
|
||||||
-export([admin_list_all/0, admin_get_by_id/1, admin_update/2, admin_delete/1]).
|
-export([admin_list_all/0, admin_get_by_id/1, admin_update/2, admin_delete/1]).
|
||||||
|
|
||||||
@@ -50,6 +50,43 @@ create_calendar(UserId, Title, Description, Confirmation, Type) ->
|
|||||||
{error, user_not_found}
|
{error, user_not_found}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @doc Создаёт дефолтный personal-календарь после активации пользователя.
|
||||||
|
%% Идемпотентно: если у владельца уже есть active personal — ok.
|
||||||
|
-spec ensure_default_calendar(UserId :: binary()) -> ok | {error, term()}.
|
||||||
|
ensure_default_calendar(UserId) ->
|
||||||
|
case has_active_personal_calendar(UserId) of
|
||||||
|
true ->
|
||||||
|
ok;
|
||||||
|
false ->
|
||||||
|
case core_user:get_by_id(UserId) of
|
||||||
|
{ok, User} ->
|
||||||
|
Title = default_calendar_title(User),
|
||||||
|
case create_calendar(UserId, Title, <<>>, manual, personal) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
Error -> Error
|
||||||
|
end;
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
has_active_personal_calendar(UserId) ->
|
||||||
|
case core_calendar:list_by_owner(UserId) of
|
||||||
|
{ok, Calendars} ->
|
||||||
|
lists:any(
|
||||||
|
fun(#calendar{type = personal}) -> true;
|
||||||
|
(_) -> false
|
||||||
|
end,
|
||||||
|
Calendars);
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
default_calendar_title(#user{nickname = Nick}) when is_binary(Nick), byte_size(Nick) > 0 ->
|
||||||
|
Nick;
|
||||||
|
default_calendar_title(_) ->
|
||||||
|
<<"Мой календарь">>.
|
||||||
|
|
||||||
%% Получение календаря с проверкой доступа
|
%% Получение календаря с проверкой доступа
|
||||||
get_calendar(UserId, CalendarId) ->
|
get_calendar(UserId, CalendarId) ->
|
||||||
case core_calendar:get_by_id(CalendarId) of
|
case core_calendar:get_by_id(CalendarId) of
|
||||||
|
|||||||
@@ -47,11 +47,27 @@ test() ->
|
|||||||
#{<<"token">> := AuthToken} = jsx:decode(list_to_binary(LoginBody), [return_maps]),
|
#{<<"token">> := AuthToken} = jsx:decode(list_to_binary(LoginBody), [return_maps]),
|
||||||
?assert(is_binary(AuthToken)),
|
?assert(is_binary(AuthToken)),
|
||||||
|
|
||||||
% 6. Повторное использование того же токена – ошибка 404
|
% 6. После активации — дефолтный приватный personal-календарь
|
||||||
|
Calendars = api_test_runner:client_get(<<"/v1/calendars">>, AuthToken),
|
||||||
|
?assertEqual(1, length(Calendars)),
|
||||||
|
[DefaultCal] = Calendars,
|
||||||
|
?assertEqual(<<"personal">>, maps:get(<<"type">>, DefaultCal)),
|
||||||
|
?assertEqual(<<>>, maps:get(<<"short_name">>, DefaultCal)),
|
||||||
|
ExpectedTitle = case string:split(Email, <<"@">>) of
|
||||||
|
[Local, _] when byte_size(Local) > 0 -> Local;
|
||||||
|
_ -> <<"Мой календарь">>
|
||||||
|
end,
|
||||||
|
?assertEqual(ExpectedTitle, maps:get(<<"title">>, DefaultCal)),
|
||||||
|
|
||||||
|
% 7. Повторное использование того же токена – ошибка 404
|
||||||
{ok, 404, _, _} = api_test_runner:client_request(post, <<"/v1/verify">>, <<>>,
|
{ok, 404, _, _} = api_test_runner:client_request(post, <<"/v1/verify">>, <<>>,
|
||||||
jsx:encode(#{<<"token">> => Token})),
|
jsx:encode(#{<<"token">> => Token})),
|
||||||
|
|
||||||
% 7. Невалидный токен – ошибка 404
|
% 7. Повторное использование того же токена – ошибка 404
|
||||||
|
{ok, 404, _, _} = api_test_runner:client_request(post, <<"/v1/verify">>, <<>>,
|
||||||
|
jsx:encode(#{<<"token">> => Token})),
|
||||||
|
|
||||||
|
% 8. Невалидный токен – ошибка 404
|
||||||
{ok, 404, _, _} = api_test_runner:client_request(post, <<"/v1/verify">>, <<>>,
|
{ok, 404, _, _} = api_test_runner:client_request(post, <<"/v1/verify">>, <<>>,
|
||||||
jsx:encode(#{<<"token">> => <<"invalid_token">>})),
|
jsx:encode(#{<<"token">> => <<"invalid_token">>})),
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ logic_calendar_test_() ->
|
|||||||
{"List calendars test", fun test_list_calendars/0},
|
{"List calendars test", fun test_list_calendars/0},
|
||||||
{"Update calendar test", fun test_update_calendar/0},
|
{"Update calendar test", fun test_update_calendar/0},
|
||||||
{"Delete calendar test", fun test_delete_calendar/0},
|
{"Delete calendar test", fun test_delete_calendar/0},
|
||||||
{"Access control test", fun test_access_control/0}
|
{"Access control test", fun test_access_control/0},
|
||||||
|
{"Ensure default calendar test", fun test_ensure_default_calendar/0}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
create_test_user() ->
|
create_test_user() ->
|
||||||
@@ -41,6 +42,7 @@ create_test_user() ->
|
|||||||
password_hash = <<"hash">>,
|
password_hash = <<"hash">>,
|
||||||
role = user,
|
role = user,
|
||||||
status = active,
|
status = active,
|
||||||
|
nickname = <<>>,
|
||||||
created_at = calendar:universal_time(),
|
created_at = calendar:universal_time(),
|
||||||
updated_at = calendar:universal_time()
|
updated_at = calendar:universal_time()
|
||||||
},
|
},
|
||||||
@@ -122,3 +124,17 @@ test_access_control() ->
|
|||||||
{ok, Frozen} = core_calendar:update(CommercialCalendar#calendar.id, [{status, frozen}]),
|
{ok, Frozen} = core_calendar:update(CommercialCalendar#calendar.id, [{status, frozen}]),
|
||||||
?assertNot(logic_calendar:can_access(OtherId, Frozen)),
|
?assertNot(logic_calendar:can_access(OtherId, Frozen)),
|
||||||
?assertNot(logic_calendar:can_access(OwnerId, Frozen)).
|
?assertNot(logic_calendar:can_access(OwnerId, Frozen)).
|
||||||
|
|
||||||
|
test_ensure_default_calendar() ->
|
||||||
|
UserId = create_test_user(),
|
||||||
|
ok = logic_calendar:ensure_default_calendar(UserId),
|
||||||
|
{ok, Calendars} = logic_calendar:list_calendars(UserId),
|
||||||
|
?assertEqual(1, length(Calendars)),
|
||||||
|
Cal = hd(Calendars),
|
||||||
|
?assertEqual(personal, Cal#calendar.type),
|
||||||
|
?assertEqual(<<>>, Cal#calendar.short_name),
|
||||||
|
?assertEqual(active, Cal#calendar.status),
|
||||||
|
?assert(byte_size(Cal#calendar.title) > 0),
|
||||||
|
ok = logic_calendar:ensure_default_calendar(UserId),
|
||||||
|
{ok, Calendars2} = logic_calendar:list_calendars(UserId),
|
||||||
|
?assertEqual(1, length(Calendars2)).
|
||||||
Reference in New Issue
Block a user