Bug fixes and implementation refinement around `ts_ext_data
`.
* Transformed the test database to match the new data model. Added ``ts_ext_data`` table and moved ``ts_user.ext_data`` values to it. * Added D0022: cascade delete ``ts_ext_data`` when ``ts_entry`` record is deleted. * Completed refactor of ``ts_api`` functions to account for extended data: * ``post_entry/3`` * ``put_entry/3`` * Created ``ts_entry:write/2`` to write extended data atomically with the entry. * Fixed copy/paste bug in ``ts_ext_data:create_table/1``. * Fixed implementation of ``ts_ext_data:set_property/3`` to be explicit about taking a record, not a reference. It then extracts the reference and passes it to the underlying implementation in ``ts_ext_data:do_set_property/3``. * Refactored ``ts_ext_data:do_set_property/3`` to take a record *reference*, not the record itself. * Refactored the ``ts_ext_data:get_property/2`` and ``ts_ext_data:get_properties/1`` functions to return key value tuple pairs, not ``ts_ext_data{}`` records.
This commit is contained in:
parent
658091e947
commit
3999e736be
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
db/test/ts_ext_data.DCD
Normal file
BIN
db/test/ts_ext_data.DCD
Normal file
Binary file not shown.
BIN
db/test/ts_ext_data.DCL
Normal file
BIN
db/test/ts_ext_data.DCL
Normal file
Binary file not shown.
Binary file not shown.
10
doc/issues/desktop/0022bn5.rst
Normal file
10
doc/issues/desktop/0022bn5.rst
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Deleting an entry should cascade delete extended data.
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
Currently the data remains in the database. It should not cause any
|
||||||
|
problems, but it is wasting space.
|
||||||
|
|
||||||
|
========= ==========
|
||||||
|
Created: 2011-06-15
|
||||||
|
Resolved: YYYY-MM-DD
|
||||||
|
========= ==========
|
@ -418,11 +418,10 @@ post_entry(YArg, Username, TimelineId) ->
|
|||||||
#ts_entry{ref = {Username, TimelineId, undefined}}, EJSON)
|
#ts_entry{ref = {Username, TimelineId, undefined}}, EJSON)
|
||||||
catch _:InputError ->
|
catch _:InputError ->
|
||||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||||
throw(make_json_400(YArg))
|
throw(make_json_400(YArg, {request_error, InputError}))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
%% TODO; should entries and their properties be created atomically?
|
case ts_entry:new(ER, ExtData) of
|
||||||
case ts_entry:new(ER) of
|
|
||||||
% record created
|
% record created
|
||||||
{ok, CreatedRecord} ->
|
{ok, CreatedRecord} ->
|
||||||
|
|
||||||
@ -445,14 +444,14 @@ put_entry(YArg, Username, TimelineId, EntryId) ->
|
|||||||
EJSON = parse_json_body(YArg),
|
EJSON = parse_json_body(YArg),
|
||||||
|
|
||||||
% parse into ts_entry record
|
% parse into ts_entry record
|
||||||
ER = try ts_json:ejson_to_record_strict(
|
{ER, ExtData} = try ts_json:ejson_to_record_strict(
|
||||||
#ts_entry{ref={Username, TimelineId, EntryId}}, EJSON)
|
#ts_entry{ref={Username, TimelineId, EntryId}}, EJSON)
|
||||||
catch _:InputError ->
|
catch _:InputError ->
|
||||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||||
throw(make_json_400(YArg))
|
throw(make_json_400(YArg))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
ts_entry:write(ER),
|
ts_entry:write(ER, ExtData),
|
||||||
make_json_200(YArg, ER).
|
make_json_200(YArg, ER).
|
||||||
|
|
||||||
delete_entry(YArg, Username, TimelineId, EntryId) ->
|
delete_entry(YArg, Username, TimelineId, EntryId) ->
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
-module(ts_entry).
|
-module(ts_entry).
|
||||||
-export([create_table/1, new/1, new/2, update/1, update/2, write/1, delete/1,
|
-export([create_table/1, new/1, new/2, update/1, update/2, write/1, write/2,
|
||||||
lookup/3, list_asc/3, list_desc/3]).
|
delete/1, lookup/3, list_asc/3, list_desc/3]).
|
||||||
|
|
||||||
-include("ts_db_records.hrl").
|
-include("ts_db_records.hrl").
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
@ -35,6 +35,13 @@ update(ER = #ts_entry{}, ExtData) when is_list(ExtData) ->
|
|||||||
|
|
||||||
write(ER = #ts_entry{}) -> mnesia:dirty_write(ER).
|
write(ER = #ts_entry{}) -> mnesia:dirty_write(ER).
|
||||||
|
|
||||||
|
write(ER = #ts_entry{}, ExtData) ->
|
||||||
|
{atomic, Result} = mnesia:transcation(fun() ->
|
||||||
|
ok = mnesia:write(ER),
|
||||||
|
ok = ts_common:do_set_ext_data(ER, ExtData)
|
||||||
|
end),
|
||||||
|
Result.
|
||||||
|
|
||||||
lookup(Username, TimelineId, EntryId) ->
|
lookup(Username, TimelineId, EntryId) ->
|
||||||
case mnesia:dirty_read(ts_entry, {Username, TimelineId, EntryId}) of
|
case mnesia:dirty_read(ts_entry, {Username, TimelineId, EntryId}) of
|
||||||
[] -> no_record;
|
[] -> no_record;
|
||||||
|
@ -4,41 +4,44 @@
|
|||||||
-include("ts_db_records.hrl").
|
-include("ts_db_records.hrl").
|
||||||
|
|
||||||
create_table(TableOpts) ->
|
create_table(TableOpts) ->
|
||||||
mnesia:create_table(ts_entry,
|
mnesia:create_table(ts_ext_data,
|
||||||
TableOpts ++ [{attributes, record_info(fields, ts_ext_data)},
|
TableOpts ++ [{attributes, record_info(fields, ts_ext_data)},
|
||||||
{type, ordered_set}]).
|
{type, ordered_set}]).
|
||||||
|
|
||||||
% set last timeline
|
% set last timeline
|
||||||
set_property(Ref=#ts_user{}, last_timeline, LastTimelineId) ->
|
set_property(Rec=#ts_user{}, last_timeline, LastTimelineId) ->
|
||||||
do_set_property(Ref, last_timeline, LastTimelineId);
|
do_set_property(Rec#ts_user.username, last_timeline, LastTimelineId);
|
||||||
|
|
||||||
% Set exclusion_list for a User account
|
% Set exclusion_list for a User account
|
||||||
set_property(Ref=#ts_user{}, entry_exclusions, ExclusionList) ->
|
set_property(Rec=#ts_user{}, entry_exclusions, ExclusionList) ->
|
||||||
do_set_property(Ref, entry_exclusions, string:join(ExclusionList, "|"));
|
do_set_property(Rec#ts_user.username, entry_exclusions, ExclusionList);
|
||||||
|
|
||||||
% Set exclusion_list for a Timeline entry
|
% Set exclusion_list for a Timeline entry
|
||||||
set_property(Ref=#ts_timeline{}, entry_exclusions, ExclusionList) ->
|
set_property(Rec=#ts_timeline{}, entry_exclusions, ExclusionList) ->
|
||||||
do_set_property(Ref, entry_exclusions, string:join(ExclusionList, "|"));
|
do_set_property(Rec#ts_timeline.ref, entry_exclusions, ExclusionList);
|
||||||
|
|
||||||
set_property(Ref, Key, Value) ->
|
set_property(Rec, Key, _Value) ->
|
||||||
throw(io_lib:format("Property '~s' not available for a ~s record.",
|
throw(io_lib:format("Property '~s' not available for a ~s record.",
|
||||||
[Key, element(1, Ref)])).
|
[Key, element(1, Rec)])).
|
||||||
|
|
||||||
get_property(Ref, PropKey) ->
|
get_property(Ref, PropKey) ->
|
||||||
{atomic, Result} = mnesia:transaction(fun() ->
|
{atomic, Result} = mnesia:transaction(fun() ->
|
||||||
case mnesia:read(ts_ext_data, {Ref, PropKey}) of
|
case mnesia:read(ts_ext_data, {Ref, PropKey}) of
|
||||||
[] -> not_set;
|
[] -> not_set;
|
||||||
[Property] -> Property
|
[Property] -> Property#ts_ext_data.value
|
||||||
end
|
end
|
||||||
end),
|
end),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
get_properties(Ref) ->
|
get_properties(Rec) ->
|
||||||
|
Ref = element(2, Rec),
|
||||||
{atomic, Result} = mnesia:transaction(fun() ->
|
{atomic, Result} = mnesia:transaction(fun() ->
|
||||||
MatchHead = #ts_ext_data{ref = {Ref, '_'}, _='_'},
|
MatchHead = #ts_ext_data{ref = {Ref, '_'}, _='_'},
|
||||||
mnesia:select(ts_ext_data, [{MatchHead, [], ['$_']}])
|
mnesia:select(ts_ext_data, [{MatchHead, [], ['$_']}])
|
||||||
end),
|
end),
|
||||||
Result.
|
lists:map(fun(ExtData = #ts_ext_data{}) ->
|
||||||
|
{Ref, Key} = ExtData#ts_ext_data.ref,
|
||||||
|
{Key, ExtData#ts_ext_data.value} end, Result).
|
||||||
|
|
||||||
do_set_property(Ref, PropKey, Val) ->
|
do_set_property(Ref, PropKey, Val) ->
|
||||||
{atomic, Result} = mnesia:transaction(fun() ->
|
{atomic, Result} = mnesia:transaction(fun() ->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user