diff --git a/src/core/core_calendar.erl b/src/core/core_calendar.erl index f65c5de..26eb238 100644 --- a/src/core/core_calendar.erl +++ b/src/core/core_calendar.erl @@ -1,6 +1,7 @@ -module(core_calendar). -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([freeze/2, unfreeze/2]). -export([count_calendars_by_date/2]). @@ -78,6 +79,28 @@ get_by_id(Id) -> [Calendar] -> {ok, Calendar} 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 Список активных календарей владельца. %%% @end diff --git a/src/handlers/handler_calendar_by_id.erl b/src/handlers/handler_calendar_by_id.erl index c9b2c82..a337165 100755 --- a/src/handlers/handler_calendar_by_id.erl +++ b/src/handlers/handler_calendar_by_id.erl @@ -121,7 +121,8 @@ get_calendar(Req) -> case logic_calendar:get_calendar(UserId, CalendarId) of {ok, 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}); {error, access_denied} -> handler_utils:send_error(Req1, 403, <<"Access denied">>); diff --git a/src/logic/logic_calendar.erl b/src/logic/logic_calendar.erl index ddc402d..dd78915 100755 --- a/src/logic/logic_calendar.erl +++ b/src/logic/logic_calendar.erl @@ -162,7 +162,7 @@ calendar_has_content(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} -> case can_access(UserId, Calendar) of true -> {ok, Calendar}; diff --git a/src/logic/logic_calendar_follow.erl b/src/logic/logic_calendar_follow.erl index 71ada5e..32bdeb7 100755 --- a/src/logic/logic_calendar_follow.erl +++ b/src/logic/logic_calendar_follow.erl @@ -15,7 +15,7 @@ -spec follow(UserId :: binary(), CalendarId :: binary()) -> {ok, #calendar_follow{}} | {error, term()}. 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}; {ok, #calendar{owner_id = UserId}} -> @@ -27,7 +27,7 @@ follow(UserId, CalendarId) -> true -> case Calendar#calendar.status of active -> - core_calendar_follow:follow(CalendarId, UserId); + core_calendar_follow:follow(Calendar#calendar.id, UserId); _ -> {error, not_found} end @@ -40,14 +40,13 @@ follow(UserId, CalendarId) -> %%%------------------------------------------------------------------- -spec unfollow(UserId :: binary(), CalendarId :: binary()) -> ok | {error, term()}. 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} -> - %% всё равно снимаем локальный follow, если был core_calendar_follow:unfollow(CalendarId, UserId); {ok, #calendar{owner_id = UserId}} -> {error, own_calendar}; - {ok, _} -> - core_calendar_follow:unfollow(CalendarId, UserId) + {ok, Calendar} -> + core_calendar_follow:unfollow(Calendar#calendar.id, UserId) end. %%%------------------------------------------------------------------- diff --git a/src/logic/logic_calendar_specialist.erl b/src/logic/logic_calendar_specialist.erl index b218580..09d67b9 100755 --- a/src/logic/logic_calendar_specialist.erl +++ b/src/logic/logic_calendar_specialist.erl @@ -10,12 +10,9 @@ -spec list(ActorId :: binary(), CalendarId :: binary()) -> {ok, [#calendar_specialist{}]} | {error, not_found | access_denied}. list(ActorId, CalendarId) -> - case core_calendar:get_by_id(CalendarId) of + case logic_calendar:get_calendar(ActorId, CalendarId) of {ok, Cal} -> - case logic_calendar:can_access(ActorId, Cal) of - true -> {ok, core_calendar_specialist:list_by_calendar(CalendarId)}; - false -> {error, access_denied} - end; + {ok, core_calendar_specialist:list_by_calendar(Cal#calendar.id)}; Error -> Error end. diff --git a/src/logic/logic_event.erl b/src/logic/logic_event.erl index 0a9869a..113ef05 100755 --- a/src/logic/logic_event.erl +++ b/src/logic/logic_event.erl @@ -25,7 +25,7 @@ create_event(UserId, CalendarId, Title, StartTime, Duration, Description) -> {reject, Words} -> {error, {content_banned, 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} -> case Desc2 of <<>> -> ok; @@ -64,7 +64,7 @@ create_recurring_event(UserId, CalendarId, Title, StartTime, Duration, RRule, De {reject, Words} -> {error, {content_banned, 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} -> case Desc2 of <<>> -> ok; @@ -184,8 +184,8 @@ get_event(UserId, EventId) -> %% Список событий календаря list_events(UserId, CalendarId) -> case logic_calendar:get_calendar(UserId, CalendarId) of - {ok, _} -> - core_event:list_by_calendar(CalendarId); + {ok, Calendar} -> + core_event:list_by_calendar(Calendar#calendar.id); Error -> Error end. diff --git a/test/api/users/user_calendar_by_id_tests.erl b/test/api/users/user_calendar_by_id_tests.erl index d4d6732..a417e2e 100644 --- a/test/api/users/user_calendar_by_id_tests.erl +++ b/test/api/users/user_calendar_by_id_tests.erl @@ -20,6 +20,7 @@ test() -> test_get_calendar(Token, CalId), test_get_calendar_guest(Token, CalId), + test_get_calendar_guest_short_name(Token, CalId), test_get_calendar_not_found(Token), test_update_calendar(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, <<>>), 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) -> ct:pal(" TEST: Get non-existent calendar (404)"), Resp = api_test_runner:client_request(get, <<"/v1/calendars/fakeid">>, Token), diff --git a/test/unit/logic_calendar_tests.erl b/test/unit/logic_calendar_tests.erl index afc5bab..c2a04bc 100755 --- a/test/unit/logic_calendar_tests.erl +++ b/test/unit/logic_calendar_tests.erl @@ -12,6 +12,7 @@ setup() -> {attributes, record_info(fields, calendar)}, {ram_copies, [node()]} ]), + {atomic, ok} = mnesia:add_table_index(calendar, short_name), mnesia:create_table(subscription, [ {attributes, record_info(fields, subscription)}, {ram_copies, [node()]} @@ -38,6 +39,7 @@ logic_calendar_test_() -> {"Create calendar test", fun test_create_calendar/0}, {"Create commercial requires subscription", fun test_create_commercial_gate/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}, {"Update calendar test", fun test_update_calendar/0}, {"Persist calendar settings (week_patterns)", fun test_update_settings/0}, @@ -104,6 +106,18 @@ test_get_calendar() -> ?assertMatch({error, access_denied}, 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() -> UserId = create_test_user(), {ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 1">>, <<"">>, manual),