fix(api): resolve public calendar GET by unique short_name.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
-module(core_calendar).
|
-module(core_calendar).
|
||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
-export([create/4, create/5, get_by_id/1, list_by_owner/1, update/2, delete/1]).
|
-export([create/4, create/5, get_by_id/1, get_by_short_name/1, get_by_id_or_short_name/1,
|
||||||
|
list_by_owner/1, update/2, delete/1]).
|
||||||
-export([count_calendars/0, list_all/0]).
|
-export([count_calendars/0, list_all/0]).
|
||||||
-export([freeze/2, unfreeze/2]).
|
-export([freeze/2, unfreeze/2]).
|
||||||
-export([count_calendars_by_date/2]).
|
-export([count_calendars_by_date/2]).
|
||||||
@@ -78,6 +79,28 @@ get_by_id(Id) ->
|
|||||||
[Calendar] -> {ok, Calendar}
|
[Calendar] -> {ok, Calendar}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% Unique public slug. Empty name is not a slug (many Default calendars).
|
||||||
|
-spec get_by_short_name(Name :: binary()) -> {ok, #calendar{}} | {error, not_found}.
|
||||||
|
get_by_short_name(<<>>) ->
|
||||||
|
{error, not_found};
|
||||||
|
get_by_short_name(Name) when is_binary(Name) ->
|
||||||
|
case mnesia:dirty_index_read(calendar, Name, #calendar.short_name) of
|
||||||
|
[] ->
|
||||||
|
{error, not_found};
|
||||||
|
Rows ->
|
||||||
|
case [C || C <- Rows, C#calendar.status =:= active] of
|
||||||
|
[C | _] -> {ok, C};
|
||||||
|
[] -> {error, not_found}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_by_id_or_short_name(IdOrSlug :: binary()) -> {ok, #calendar{}} | {error, not_found}.
|
||||||
|
get_by_id_or_short_name(IdOrSlug) ->
|
||||||
|
case get_by_id(IdOrSlug) of
|
||||||
|
{ok, C} -> {ok, C};
|
||||||
|
{error, not_found} -> get_by_short_name(IdOrSlug)
|
||||||
|
end.
|
||||||
|
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
%%% @doc Список активных календарей владельца.
|
%%% @doc Список активных календарей владельца.
|
||||||
%%% @end
|
%%% @end
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ get_calendar(Req) ->
|
|||||||
case logic_calendar:get_calendar(UserId, CalendarId) of
|
case logic_calendar:get_calendar(UserId, CalendarId) of
|
||||||
{ok, Calendar} ->
|
{ok, Calendar} ->
|
||||||
Json0 = handler_utils:calendar_to_json(Calendar),
|
Json0 = handler_utils:calendar_to_json(Calendar),
|
||||||
Following = UserId =/= <<>> andalso logic_calendar_follow:is_following(UserId, CalendarId),
|
Following = UserId =/= <<>> andalso
|
||||||
|
logic_calendar_follow:is_following(UserId, Calendar#calendar.id),
|
||||||
handler_utils:send_json(Req1, 200, Json0#{following => Following});
|
handler_utils:send_json(Req1, 200, Json0#{following => Following});
|
||||||
{error, access_denied} ->
|
{error, access_denied} ->
|
||||||
handler_utils:send_error(Req1, 403, <<"Access denied">>);
|
handler_utils:send_error(Req1, 403, <<"Access denied">>);
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ calendar_has_content(CalendarId) ->
|
|||||||
|
|
||||||
%% Получение календаря с проверкой доступа
|
%% Получение календаря с проверкой доступа
|
||||||
get_calendar(UserId, CalendarId) ->
|
get_calendar(UserId, CalendarId) ->
|
||||||
case core_calendar:get_by_id(CalendarId) of
|
case core_calendar:get_by_id_or_short_name(CalendarId) of
|
||||||
{ok, Calendar} ->
|
{ok, Calendar} ->
|
||||||
case can_access(UserId, Calendar) of
|
case can_access(UserId, Calendar) of
|
||||||
true -> {ok, Calendar};
|
true -> {ok, Calendar};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
-spec follow(UserId :: binary(), CalendarId :: binary()) ->
|
-spec follow(UserId :: binary(), CalendarId :: binary()) ->
|
||||||
{ok, #calendar_follow{}} | {error, term()}.
|
{ok, #calendar_follow{}} | {error, term()}.
|
||||||
follow(UserId, CalendarId) ->
|
follow(UserId, CalendarId) ->
|
||||||
case core_calendar:get_by_id(CalendarId) of
|
case core_calendar:get_by_id_or_short_name(CalendarId) of
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
{ok, #calendar{owner_id = UserId}} ->
|
{ok, #calendar{owner_id = UserId}} ->
|
||||||
@@ -27,7 +27,7 @@ follow(UserId, CalendarId) ->
|
|||||||
true ->
|
true ->
|
||||||
case Calendar#calendar.status of
|
case Calendar#calendar.status of
|
||||||
active ->
|
active ->
|
||||||
core_calendar_follow:follow(CalendarId, UserId);
|
core_calendar_follow:follow(Calendar#calendar.id, UserId);
|
||||||
_ ->
|
_ ->
|
||||||
{error, not_found}
|
{error, not_found}
|
||||||
end
|
end
|
||||||
@@ -40,14 +40,13 @@ follow(UserId, CalendarId) ->
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-spec unfollow(UserId :: binary(), CalendarId :: binary()) -> ok | {error, term()}.
|
-spec unfollow(UserId :: binary(), CalendarId :: binary()) -> ok | {error, term()}.
|
||||||
unfollow(UserId, CalendarId) ->
|
unfollow(UserId, CalendarId) ->
|
||||||
case core_calendar:get_by_id(CalendarId) of
|
case core_calendar:get_by_id_or_short_name(CalendarId) of
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
%% всё равно снимаем локальный follow, если был
|
|
||||||
core_calendar_follow:unfollow(CalendarId, UserId);
|
core_calendar_follow:unfollow(CalendarId, UserId);
|
||||||
{ok, #calendar{owner_id = UserId}} ->
|
{ok, #calendar{owner_id = UserId}} ->
|
||||||
{error, own_calendar};
|
{error, own_calendar};
|
||||||
{ok, _} ->
|
{ok, Calendar} ->
|
||||||
core_calendar_follow:unfollow(CalendarId, UserId)
|
core_calendar_follow:unfollow(Calendar#calendar.id, UserId)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|||||||
@@ -10,12 +10,9 @@
|
|||||||
-spec list(ActorId :: binary(), CalendarId :: binary()) ->
|
-spec list(ActorId :: binary(), CalendarId :: binary()) ->
|
||||||
{ok, [#calendar_specialist{}]} | {error, not_found | access_denied}.
|
{ok, [#calendar_specialist{}]} | {error, not_found | access_denied}.
|
||||||
list(ActorId, CalendarId) ->
|
list(ActorId, CalendarId) ->
|
||||||
case core_calendar:get_by_id(CalendarId) of
|
case logic_calendar:get_calendar(ActorId, CalendarId) of
|
||||||
{ok, Cal} ->
|
{ok, Cal} ->
|
||||||
case logic_calendar:can_access(ActorId, Cal) of
|
{ok, core_calendar_specialist:list_by_calendar(Cal#calendar.id)};
|
||||||
true -> {ok, core_calendar_specialist:list_by_calendar(CalendarId)};
|
|
||||||
false -> {error, access_denied}
|
|
||||||
end;
|
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ create_event(UserId, CalendarId, Title, StartTime, Duration, Description) ->
|
|||||||
{reject, Words} ->
|
{reject, Words} ->
|
||||||
{error, {content_banned, Words}};
|
{error, {content_banned, Words}};
|
||||||
{ok, Action, [Title2, Desc2], Words} ->
|
{ok, Action, [Title2, Desc2], Words} ->
|
||||||
case core_event:create(CalendarId, Title2, StartTime, Duration) of
|
case core_event:create(Calendar#calendar.id, Title2, StartTime, Duration) of
|
||||||
{ok, Event} ->
|
{ok, Event} ->
|
||||||
case Desc2 of
|
case Desc2 of
|
||||||
<<>> -> ok;
|
<<>> -> ok;
|
||||||
@@ -64,7 +64,7 @@ create_recurring_event(UserId, CalendarId, Title, StartTime, Duration, RRule, De
|
|||||||
{reject, Words} ->
|
{reject, Words} ->
|
||||||
{error, {content_banned, Words}};
|
{error, {content_banned, Words}};
|
||||||
{ok, Action, [Title2, Desc2], Words} ->
|
{ok, Action, [Title2, Desc2], Words} ->
|
||||||
case core_event:create_recurring(CalendarId, Title2, StartTime, Duration, RRule) of
|
case core_event:create_recurring(Calendar#calendar.id, Title2, StartTime, Duration, RRule) of
|
||||||
{ok, Event} ->
|
{ok, Event} ->
|
||||||
case Desc2 of
|
case Desc2 of
|
||||||
<<>> -> ok;
|
<<>> -> ok;
|
||||||
@@ -184,8 +184,8 @@ get_event(UserId, EventId) ->
|
|||||||
%% Список событий календаря
|
%% Список событий календаря
|
||||||
list_events(UserId, CalendarId) ->
|
list_events(UserId, CalendarId) ->
|
||||||
case logic_calendar:get_calendar(UserId, CalendarId) of
|
case logic_calendar:get_calendar(UserId, CalendarId) of
|
||||||
{ok, _} ->
|
{ok, Calendar} ->
|
||||||
core_event:list_by_calendar(CalendarId);
|
core_event:list_by_calendar(Calendar#calendar.id);
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ test() ->
|
|||||||
|
|
||||||
test_get_calendar(Token, CalId),
|
test_get_calendar(Token, CalId),
|
||||||
test_get_calendar_guest(Token, CalId),
|
test_get_calendar_guest(Token, CalId),
|
||||||
|
test_get_calendar_guest_short_name(Token, CalId),
|
||||||
test_get_calendar_not_found(Token),
|
test_get_calendar_not_found(Token),
|
||||||
test_update_calendar(Token, CalId),
|
test_update_calendar(Token, CalId),
|
||||||
test_update_calendar_settings(Token, CalId),
|
test_update_calendar_settings(Token, CalId),
|
||||||
@@ -49,6 +50,20 @@ test_get_calendar_guest(Token, CalId) ->
|
|||||||
{ok, 403, _, _} = api_test_runner:client_request(get, PPath, <<>>),
|
{ok, 403, _, _} = api_test_runner:client_request(get, PPath, <<>>),
|
||||||
ct:pal(" OK: guest commercial 200, personal 403").
|
ct:pal(" OK: guest commercial 200, personal 403").
|
||||||
|
|
||||||
|
test_get_calendar_guest_short_name(Token, CalId) ->
|
||||||
|
ct:pal(" TEST: Guest GET commercial by short_name"),
|
||||||
|
PathId = <<"/v1/calendars/", CalId/binary>>,
|
||||||
|
Slug2 = <<"sn", (integer_to_binary(erlang:unique_integer([positive])))/binary>>,
|
||||||
|
_ = api_test_runner:client_put(PathId, Token, #{short_name => Slug2}),
|
||||||
|
PathSlug = <<"/v1/calendars/", Slug2/binary>>,
|
||||||
|
{ok, 200, _, Body} = api_test_runner:client_request(get, PathSlug, <<>>),
|
||||||
|
Dec = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||||
|
?assertEqual(CalId, maps:get(<<"id">>, Dec)),
|
||||||
|
?assertEqual(Slug2, maps:get(<<"short_name">>, Dec)),
|
||||||
|
EvPath = <<"/v1/calendars/", Slug2/binary, "/events">>,
|
||||||
|
{ok, 200, _, _} = api_test_runner:client_request(get, EvPath, <<>>),
|
||||||
|
ct:pal(" OK: guest GET by short_name").
|
||||||
|
|
||||||
test_get_calendar_not_found(Token) ->
|
test_get_calendar_not_found(Token) ->
|
||||||
ct:pal(" TEST: Get non-existent calendar (404)"),
|
ct:pal(" TEST: Get non-existent calendar (404)"),
|
||||||
Resp = api_test_runner:client_request(get, <<"/v1/calendars/fakeid">>, Token),
|
Resp = api_test_runner:client_request(get, <<"/v1/calendars/fakeid">>, Token),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ setup() ->
|
|||||||
{attributes, record_info(fields, calendar)},
|
{attributes, record_info(fields, calendar)},
|
||||||
{ram_copies, [node()]}
|
{ram_copies, [node()]}
|
||||||
]),
|
]),
|
||||||
|
{atomic, ok} = mnesia:add_table_index(calendar, short_name),
|
||||||
mnesia:create_table(subscription, [
|
mnesia:create_table(subscription, [
|
||||||
{attributes, record_info(fields, subscription)},
|
{attributes, record_info(fields, subscription)},
|
||||||
{ram_copies, [node()]}
|
{ram_copies, [node()]}
|
||||||
@@ -38,6 +39,7 @@ logic_calendar_test_() ->
|
|||||||
{"Create calendar test", fun test_create_calendar/0},
|
{"Create calendar test", fun test_create_calendar/0},
|
||||||
{"Create commercial requires subscription", fun test_create_commercial_gate/0},
|
{"Create commercial requires subscription", fun test_create_commercial_gate/0},
|
||||||
{"Get calendar test", fun test_get_calendar/0},
|
{"Get calendar test", fun test_get_calendar/0},
|
||||||
|
{"Get calendar by short_name", fun test_get_calendar_by_short_name/0},
|
||||||
{"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},
|
||||||
{"Persist calendar settings (week_patterns)", fun test_update_settings/0},
|
{"Persist calendar settings (week_patterns)", fun test_update_settings/0},
|
||||||
@@ -104,6 +106,18 @@ test_get_calendar() ->
|
|||||||
?assertMatch({error, access_denied},
|
?assertMatch({error, access_denied},
|
||||||
logic_calendar:get_calendar(OtherUserId, Calendar#calendar.id)).
|
logic_calendar:get_calendar(OtherUserId, Calendar#calendar.id)).
|
||||||
|
|
||||||
|
test_get_calendar_by_short_name() ->
|
||||||
|
UserId = create_test_user(),
|
||||||
|
{ok, _} = logic_subscription:start_trial(UserId),
|
||||||
|
{ok, Cal} = logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial),
|
||||||
|
Slug = <<"bizcal-unit-", (Cal#calendar.id)/binary>>,
|
||||||
|
{ok, _} = core_calendar:update(Cal#calendar.id, [{short_name, Slug}]),
|
||||||
|
{ok, Found} = logic_calendar:get_calendar(<<>>, Slug),
|
||||||
|
?assertEqual(Cal#calendar.id, Found#calendar.id),
|
||||||
|
{ok, Found2} = logic_calendar:get_calendar(UserId, Slug),
|
||||||
|
?assertEqual(Cal#calendar.id, Found2#calendar.id),
|
||||||
|
?assertEqual({error, not_found}, logic_calendar:get_calendar(<<>>, <<"no-such-slug">>)).
|
||||||
|
|
||||||
test_list_calendars() ->
|
test_list_calendars() ->
|
||||||
UserId = create_test_user(),
|
UserId = create_test_user(),
|
||||||
{ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 1">>, <<"">>, manual),
|
{ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 1">>, <<"">>, manual),
|
||||||
|
|||||||
Reference in New Issue
Block a user