fix(share): register calendar_share migration and rebuild old PK schema. Refs EventHub/EventHubBack#73
This commit is contained in:
@@ -148,5 +148,9 @@ map_decide_error(Req, not_pending) ->
|
|||||||
handler_utils:send_error(Req, 409, <<"Invite is not pending">>);
|
handler_utils:send_error(Req, 409, <<"Invite is not pending">>);
|
||||||
map_decide_error(Req, expired) ->
|
map_decide_error(Req, expired) ->
|
||||||
handler_utils:send_error(Req, 410, <<"Invite expired">>);
|
handler_utils:send_error(Req, 410, <<"Invite expired">>);
|
||||||
map_decide_error(Req, _) ->
|
map_decide_error(Req, {no_exists, Table}) ->
|
||||||
handler_utils:send_error(Req, 500, <<"Internal server error">>).
|
handler_utils:report_and_send_error(Req, 503,
|
||||||
|
<<"Schema not ready">>, #{reason => no_exists, table => Table});
|
||||||
|
map_decide_error(Req, Reason) ->
|
||||||
|
handler_utils:report_and_send_error(Req, 500,
|
||||||
|
<<"Internal server error">>, #{reason => Reason}).
|
||||||
|
|||||||
@@ -28,7 +28,10 @@
|
|||||||
'20260720210000_calendar_follow',
|
'20260720210000_calendar_follow',
|
||||||
'20260722150000_calendar_specialist_id',
|
'20260722150000_calendar_specialist_id',
|
||||||
'20260722190000_specialist_invite',
|
'20260722190000_specialist_invite',
|
||||||
'20260730120000_recurrence_exception_bag'
|
'20260730120000_recurrence_exception_bag',
|
||||||
|
'20260814193000_waitlist_entry',
|
||||||
|
'20260815200000_push_subscription',
|
||||||
|
'20260815220000_calendar_share_invite'
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% ------------------------------
|
%% ------------------------------
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
%% @doc calendar_share: id PK + mirror_to_default; create calendar_share_invite.
|
%% @doc calendar_share: id PK + mirror_to_default; create calendar_share_invite.
|
||||||
|
%% Old schema PK was calendar_id — mnesia:transform_table cannot change the key,
|
||||||
|
%% so we rebuild the table (feature was unused until Back#73).
|
||||||
-module('20260815220000_calendar_share_invite').
|
-module('20260815220000_calendar_share_invite').
|
||||||
|
|
||||||
-export([up/0, down/0]).
|
-export([up/0, down/0]).
|
||||||
@@ -6,7 +8,7 @@
|
|||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
|
|
||||||
up() ->
|
up() ->
|
||||||
transform_calendar_share(),
|
rebuild_calendar_share_if_needed(),
|
||||||
ensure_index(calendar_share, calendar_id),
|
ensure_index(calendar_share, calendar_id),
|
||||||
ensure_index(calendar_share, user_id),
|
ensure_index(calendar_share, user_id),
|
||||||
ensure_table(calendar_share_invite, record_info(fields, calendar_share_invite)),
|
ensure_table(calendar_share_invite, record_info(fields, calendar_share_invite)),
|
||||||
@@ -21,7 +23,7 @@ down() ->
|
|||||||
_ = mnesia:delete_table(calendar_share_invite),
|
_ = mnesia:delete_table(calendar_share_invite),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
transform_calendar_share() ->
|
rebuild_calendar_share_if_needed() ->
|
||||||
case lists:member(calendar_share, mnesia:system_info(tables)) of
|
case lists:member(calendar_share, mnesia:system_info(tables)) of
|
||||||
false ->
|
false ->
|
||||||
ensure_table(calendar_share, record_info(fields, calendar_share));
|
ensure_table(calendar_share, record_info(fields, calendar_share));
|
||||||
@@ -31,36 +33,50 @@ transform_calendar_share() ->
|
|||||||
true ->
|
true ->
|
||||||
ok;
|
ok;
|
||||||
false ->
|
false ->
|
||||||
Fun = fun(Rec) ->
|
Rows = dump_calendar_share_rows(),
|
||||||
case tuple_to_list(Rec) of
|
case mnesia:delete_table(calendar_share) of
|
||||||
[calendar_share, CalId, UserId, Rights] ->
|
|
||||||
#calendar_share{
|
|
||||||
id = infra_utils:generate_id(16),
|
|
||||||
calendar_id = CalId,
|
|
||||||
user_id = UserId,
|
|
||||||
rights = Rights,
|
|
||||||
mirror_to_default = false
|
|
||||||
};
|
|
||||||
[calendar_share, Id, CalId, UserId, Rights] ->
|
|
||||||
#calendar_share{
|
|
||||||
id = Id,
|
|
||||||
calendar_id = CalId,
|
|
||||||
user_id = UserId,
|
|
||||||
rights = Rights,
|
|
||||||
mirror_to_default = false
|
|
||||||
};
|
|
||||||
_ ->
|
|
||||||
Rec
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
case mnesia:transform_table(calendar_share, Fun,
|
|
||||||
record_info(fields, calendar_share)) of
|
|
||||||
{atomic, ok} -> ok;
|
{atomic, ok} -> ok;
|
||||||
{aborted, Reason} -> error({transform_calendar_share_failed, Reason})
|
{aborted, Reason} -> error({delete_calendar_share_failed, Reason})
|
||||||
end
|
end,
|
||||||
|
ensure_table(calendar_share, record_info(fields, calendar_share)),
|
||||||
|
case mnesia:wait_for_tables([calendar_share], 30000) of
|
||||||
|
ok -> ok;
|
||||||
|
{timeout, _} -> error({wait_calendar_share_timeout})
|
||||||
|
end,
|
||||||
|
lists:foreach(fun(Rec) -> ok = mnesia:dirty_write(Rec) end, Rows),
|
||||||
|
ok
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
dump_calendar_share_rows() ->
|
||||||
|
Raw = try mnesia:dirty_select(calendar_share, [{'_', [], ['$_']}])
|
||||||
|
catch _:_ -> []
|
||||||
|
end,
|
||||||
|
[transform_row(R) || R <- Raw].
|
||||||
|
|
||||||
|
transform_row({calendar_share, CalId, UserId, Rights})
|
||||||
|
when is_binary(CalId), is_binary(UserId) ->
|
||||||
|
#calendar_share{
|
||||||
|
id = infra_utils:generate_id(16),
|
||||||
|
calendar_id = CalId,
|
||||||
|
user_id = UserId,
|
||||||
|
rights = Rights,
|
||||||
|
mirror_to_default = false
|
||||||
|
};
|
||||||
|
transform_row({calendar_share, Id, CalId, UserId, Rights})
|
||||||
|
when is_binary(Id), is_binary(CalId), is_binary(UserId) ->
|
||||||
|
#calendar_share{
|
||||||
|
id = Id,
|
||||||
|
calendar_id = CalId,
|
||||||
|
user_id = UserId,
|
||||||
|
rights = Rights,
|
||||||
|
mirror_to_default = false
|
||||||
|
};
|
||||||
|
transform_row(#calendar_share{} = Rec) ->
|
||||||
|
Rec;
|
||||||
|
transform_row(Other) ->
|
||||||
|
error({unexpected_calendar_share_row, Other}).
|
||||||
|
|
||||||
ensure_table(Table, Attrs) ->
|
ensure_table(Table, Attrs) ->
|
||||||
case lists:member(Table, mnesia:system_info(tables)) of
|
case lists:member(Table, mnesia:system_info(tables)) of
|
||||||
true ->
|
true ->
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
-module(calendar_share_migration_tests).
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include("records.hrl").
|
||||||
|
|
||||||
|
setup() ->
|
||||||
|
mnesia:stop(),
|
||||||
|
mnesia:delete_schema([node()]),
|
||||||
|
mnesia:create_schema([node()]),
|
||||||
|
mnesia:start(),
|
||||||
|
{atomic, ok} = mnesia:create_table(calendar_share, [
|
||||||
|
{ram_copies, [node()]},
|
||||||
|
{attributes, [calendar_id, user_id, rights]}
|
||||||
|
]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
cleanup(_) ->
|
||||||
|
catch mnesia:delete_table(calendar_share),
|
||||||
|
catch mnesia:delete_table(calendar_share_invite),
|
||||||
|
mnesia:stop(),
|
||||||
|
mnesia:delete_schema([node()]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
calendar_share_migration_test_() ->
|
||||||
|
{foreach, fun setup/0, fun cleanup/1, [
|
||||||
|
{"transforms old calendar_share to id+mirror", fun test_transform_old_share/0},
|
||||||
|
{"idempotent when already new schema", fun test_idempotent_new_schema/0}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
test_transform_old_share() ->
|
||||||
|
%% Pre-#73 row shape: PK = calendar_id (no id / mirror_to_default).
|
||||||
|
ok = mnesia:dirty_write({calendar_share, <<"cal1">>, <<"u1">>, write}),
|
||||||
|
?assertEqual([calendar_id, user_id, rights],
|
||||||
|
mnesia:table_info(calendar_share, attributes)),
|
||||||
|
ok = '20260815220000_calendar_share_invite':up(),
|
||||||
|
?assertEqual(record_info(fields, calendar_share),
|
||||||
|
mnesia:table_info(calendar_share, attributes)),
|
||||||
|
[Rec] = mnesia:dirty_match_object(#calendar_share{calendar_id = <<"cal1">>, _ = '_'}),
|
||||||
|
?assertMatch(#calendar_share{user_id = <<"u1">>, rights = write,
|
||||||
|
mirror_to_default = false}, Rec),
|
||||||
|
?assert(is_binary(Rec#calendar_share.id)),
|
||||||
|
?assert(lists:member(calendar_share_invite, mnesia:system_info(tables))),
|
||||||
|
{ok, Share} = core_calendar_share:upsert(<<"cal2">>, <<"u2">>, admin, true),
|
||||||
|
?assertEqual(admin, Share#calendar_share.rights),
|
||||||
|
?assertEqual(true, Share#calendar_share.mirror_to_default).
|
||||||
|
|
||||||
|
test_idempotent_new_schema() ->
|
||||||
|
ok = '20260815220000_calendar_share_invite':up(),
|
||||||
|
ok = '20260815220000_calendar_share_invite':up(),
|
||||||
|
?assertEqual(record_info(fields, calendar_share),
|
||||||
|
mnesia:table_info(calendar_share, attributes)).
|
||||||
@@ -14,7 +14,10 @@
|
|||||||
"20260720210000_calendar_follow",
|
"20260720210000_calendar_follow",
|
||||||
"20260722150000_calendar_specialist_id",
|
"20260722150000_calendar_specialist_id",
|
||||||
"20260722190000_specialist_invite",
|
"20260722190000_specialist_invite",
|
||||||
"20260730120000_recurrence_exception_bag"
|
"20260730120000_recurrence_exception_bag",
|
||||||
|
"20260814193000_waitlist_entry",
|
||||||
|
"20260815200000_push_subscription",
|
||||||
|
"20260815220000_calendar_share_invite"
|
||||||
]).
|
]).
|
||||||
|
|
||||||
setup() ->
|
setup() ->
|
||||||
|
|||||||
Reference in New Issue
Block a user