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">>);
|
||||
map_decide_error(Req, expired) ->
|
||||
handler_utils:send_error(Req, 410, <<"Invite expired">>);
|
||||
map_decide_error(Req, _) ->
|
||||
handler_utils:send_error(Req, 500, <<"Internal server error">>).
|
||||
map_decide_error(Req, {no_exists, Table}) ->
|
||||
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',
|
||||
'20260722150000_calendar_specialist_id',
|
||||
'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.
|
||||
%% 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').
|
||||
|
||||
-export([up/0, down/0]).
|
||||
@@ -6,7 +8,7 @@
|
||||
-include("records.hrl").
|
||||
|
||||
up() ->
|
||||
transform_calendar_share(),
|
||||
rebuild_calendar_share_if_needed(),
|
||||
ensure_index(calendar_share, calendar_id),
|
||||
ensure_index(calendar_share, user_id),
|
||||
ensure_table(calendar_share_invite, record_info(fields, calendar_share_invite)),
|
||||
@@ -21,7 +23,7 @@ down() ->
|
||||
_ = mnesia:delete_table(calendar_share_invite),
|
||||
ok.
|
||||
|
||||
transform_calendar_share() ->
|
||||
rebuild_calendar_share_if_needed() ->
|
||||
case lists:member(calendar_share, mnesia:system_info(tables)) of
|
||||
false ->
|
||||
ensure_table(calendar_share, record_info(fields, calendar_share));
|
||||
@@ -31,36 +33,50 @@ transform_calendar_share() ->
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
Fun = fun(Rec) ->
|
||||
case tuple_to_list(Rec) 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
|
||||
Rows = dump_calendar_share_rows(),
|
||||
case mnesia:delete_table(calendar_share) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Reason} -> error({transform_calendar_share_failed, Reason})
|
||||
end
|
||||
{aborted, Reason} -> error({delete_calendar_share_failed, Reason})
|
||||
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.
|
||||
|
||||
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) ->
|
||||
case lists:member(Table, mnesia:system_info(tables)) of
|
||||
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",
|
||||
"20260722150000_calendar_specialist_id",
|
||||
"20260722190000_specialist_invite",
|
||||
"20260730120000_recurrence_exception_bag"
|
||||
"20260730120000_recurrence_exception_bag",
|
||||
"20260814193000_waitlist_entry",
|
||||
"20260815200000_push_subscription",
|
||||
"20260815220000_calendar_share_invite"
|
||||
]).
|
||||
|
||||
setup() ->
|
||||
|
||||
Reference in New Issue
Block a user