Fixing problems introduced by the model change.
* Many calls to ``ts_ext_data:get_properties/1`` from ``ts_api`` were passing the record reference, not the record itself. Fixed. * Created ``ts_api:put_user/2`` which updates a ``ts_user`` record. This is needed specifically for updating the ``liat_timeline`` extended data property. * Removed unreachable code in ``ts_api:post_entry/3`` * Fixed return value of some ``ts_user`` functions to use the ``{Status, Value}`` convention. TODO: make sure all calls are using the same convention. * Fixed error message formatting in ``ts_ext_data:set_property/3``. * Added clauses to ``ts_json:ejson_to_record/2``, ``ts_json:ejson_to_record_strict/2`` and ``ts_json:construct_record/3`` to handle the ``ts_user`` record type. * Added code to ``AppView.loadInitialData`` and ``AppView.selectTimeline`` to support the ``last_timeline`` extended data property.
This commit is contained in:
@ -22,6 +22,7 @@ out(YArg) ->
|
||||
{'EXIT', Err} ->
|
||||
% TODO: log error internally
|
||||
error_logger:error_report("TimeStamper: ~p", [Err]),
|
||||
io:format("Error: ~n~p", [Err]),
|
||||
make_json_500(YArg, Err);
|
||||
Other -> Other
|
||||
end.
|
||||
@ -211,7 +212,7 @@ get_user_summary(YArg, Username) ->
|
||||
User ->
|
||||
|
||||
% get user extended data properties
|
||||
UserExtData = ts_ext_data:get_properties(Username),
|
||||
UserExtData = ts_ext_data:get_properties(User),
|
||||
% convert to intermediate JSON form
|
||||
EJSONUser = ts_json:record_to_ejson(User, UserExtData),
|
||||
|
||||
@ -223,7 +224,7 @@ get_user_summary(YArg, Username) ->
|
||||
lists:map(
|
||||
fun(Timeline) ->
|
||||
ts_json:record_to_ejson(Timeline,
|
||||
ts_ext_data:get_properties(Timeline#ts_timeline.ref))
|
||||
ts_ext_data:get_properties(Timeline))
|
||||
end,
|
||||
Timelines)},
|
||||
|
||||
@ -246,6 +247,25 @@ get_user(YArg, Username) ->
|
||||
User -> make_json_200(YArg, User)
|
||||
end.
|
||||
|
||||
put_user(YArg, Username) ->
|
||||
|
||||
% parse the POST data
|
||||
EJSON = parse_json_body(YArg),
|
||||
|
||||
{UR, ExtData} =
|
||||
try ts_user:ejson_to_record_strict(#ts_user{username=Username}, EJSON)
|
||||
catch throw:{InputError, StackTrace} ->
|
||||
error_logger:error_report("Bad input in put_user/2: ~p",
|
||||
[InputError]),
|
||||
throw(make_json_400(YArg, [{request_error, InputError}]))
|
||||
end,
|
||||
|
||||
% update the record (we do not support creating users via the API right now
|
||||
{ok, UpdatedRec} = ts_user:update(UR, ExtData),
|
||||
|
||||
% return a 200
|
||||
make_json_200(YArg, UpdatedRec).
|
||||
|
||||
list_timelines(YArg, Username) ->
|
||||
% pull out the POST data
|
||||
QueryData = yaws_api:parse_query(YArg),
|
||||
@ -270,7 +290,7 @@ list_timelines(YArg, Username) ->
|
||||
EJSONTimelines = {array, lists:map(
|
||||
fun (Timeline) ->
|
||||
ts_json:record_to_ejson(Timeline,
|
||||
ts_ext_data:get_properties(Timeline#ts_timeline.ref))
|
||||
ts_ext_data:get_properties(Timeline))
|
||||
end,
|
||||
Timelines)},
|
||||
|
||||
@ -301,7 +321,7 @@ put_timeline(YArg, Username, TimelineId) ->
|
||||
% we can not parse it, tell the user
|
||||
catch throw:{InputError, _StackTrace} ->
|
||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||
throw(make_json_400(YArg, {request_error, InputError}))
|
||||
throw(make_json_400(YArg, [{request_error, InputError}]))
|
||||
end,
|
||||
|
||||
% write the changes.
|
||||
@ -355,7 +375,7 @@ list_entries(YArg, Username, TimelineId) ->
|
||||
EJSONEntries = {array, lists:map(
|
||||
fun (Entry) ->
|
||||
ts_json:record_to_ejson(Entry,
|
||||
ts_ext_data:get_properties(Entry#ts_entry.ref))
|
||||
ts_ext_data:get_properties(Entry))
|
||||
end,
|
||||
Entries)},
|
||||
|
||||
@ -391,7 +411,7 @@ list_entries(YArg, Username, TimelineId) ->
|
||||
EJSONEntries = {array, lists:map(
|
||||
fun (Entry) ->
|
||||
ts_json:record_to_ejson(Entry,
|
||||
ts_ext_data:get_properties(Entry#ts_entry.ref))
|
||||
ts_ext_data:get_properties(Entry))
|
||||
end,
|
||||
Entries)},
|
||||
|
||||
@ -418,7 +438,7 @@ post_entry(YArg, Username, TimelineId) ->
|
||||
#ts_entry{ref = {Username, TimelineId, undefined}}, EJSON)
|
||||
catch _:InputError ->
|
||||
error_logger:error_report("Bad input: ~p", [InputError]),
|
||||
throw(make_json_400(YArg, {request_error, InputError}))
|
||||
throw(make_json_400(YArg, [{request_error, InputError}]))
|
||||
end,
|
||||
|
||||
case ts_entry:new(ER, ExtData) of
|
||||
@ -427,14 +447,8 @@ post_entry(YArg, Username, TimelineId) ->
|
||||
|
||||
[{status, 201}, make_json_200(YArg, CreatedRecord)];
|
||||
|
||||
% will not create, record exists
|
||||
{error, {record_exists, ExistingRecord}} ->
|
||||
JSONResponse = json:encode(ts_json:record_to_ejson(ExistingRecord)),
|
||||
|
||||
{content, "application/json", JSONResponse};
|
||||
|
||||
OtherError ->
|
||||
error_logger:error_report("TimeStamper: Could not create entry: ~p", [OtherError]),
|
||||
error_logger:error_report("Could not create entry: ~p", [OtherError]),
|
||||
make_json_500(YArg, OtherError)
|
||||
end.
|
||||
|
||||
@ -486,8 +500,9 @@ parse_json_body(YArg) ->
|
||||
|
||||
%% Create a JSON 200 response.
|
||||
make_json_200(_YArg, Record) ->
|
||||
RecordExtData = ts_ext_data:get_properties(element(2, Record)),
|
||||
JSONResponse = json:encode(ts_json:record_to_ejson(Record, RecordExtData)),
|
||||
RecordExtData = ts_ext_data:get_properties(Record),
|
||||
EJSON = ts_json:record_to_ejson(Record, RecordExtData),
|
||||
JSONResponse = json:encode(EJSON),
|
||||
{content, "application/json", JSONResponse}.
|
||||
|
||||
make_json_400(YArg) -> make_json_400(YArg, []).
|
||||
@ -532,14 +547,14 @@ make_json_405(_YArg, Fields) ->
|
||||
end,
|
||||
|
||||
% add the path they requested
|
||||
% F2 = F1 ++ [{path, io_lib:format("~s", [(YArg#arg.req)#http_request.path])}],
|
||||
% F2 = F1 ++ [{path, io_lib:format("~p", [(YArg#arg.req)#http_request.path])}],
|
||||
|
||||
[{status, 405}, {content, "application/json", json:encode({struct, F1})}].
|
||||
|
||||
make_json_500(_YArg, Error) ->
|
||||
EJSON = {struct, [
|
||||
{status, "internal server error"},
|
||||
{error, io_lib:format("~s", [Error])}]},
|
||||
{error, lists:flatten(io_lib:format("~p", [Error]))}]},
|
||||
[{status, 500}, {content, "application/json", json:encode(EJSON)}].
|
||||
|
||||
make_json_500(_YArg) ->
|
||||
|
@ -100,10 +100,9 @@ list(Query, Start, Length) ->
|
||||
|
||||
% should be wrapped in a transaction
|
||||
do_set_ext_data(Record, ExtData) when is_list(ExtData) ->
|
||||
Ref = element(2, Record),
|
||||
lists:foreach(
|
||||
fun({Key, Val}) ->
|
||||
{atomic, ok} = ts_ext_data:set_property(Ref, Key, Val)
|
||||
{atomic, ok} = ts_ext_data:set_property(Record, Key, Val)
|
||||
end,
|
||||
ExtData),
|
||||
ok.
|
||||
|
@ -19,7 +19,7 @@ new(ER = #ts_entry{}, ExtData) when is_list(ExtData) ->
|
||||
NewRow = do_new(ER),
|
||||
ts_common:do_set_ext_data(ER, ExtData),
|
||||
NewRow end),
|
||||
NewRow.
|
||||
{ok, NewRow}.
|
||||
|
||||
do_new(ER = #ts_entry{}) ->
|
||||
{Username, TimelineId, _} = ER#ts_entry.ref,
|
||||
|
@ -21,8 +21,8 @@ set_property(Rec=#ts_timeline{}, entry_exclusions, ExclusionList) ->
|
||||
do_set_property(Rec#ts_timeline.ref, entry_exclusions, ExclusionList);
|
||||
|
||||
set_property(Rec, Key, _Value) ->
|
||||
throw(io_lib:format("Property '~s' not available for a ~s record.",
|
||||
[Key, element(1, Rec)])).
|
||||
throw(lists:flatten(io_lib:format("Property '~s' not available for a ~s record.",
|
||||
[Key, element(1, Rec)]))).
|
||||
|
||||
get_property(Ref, PropKey) ->
|
||||
{atomic, Result} = mnesia:transaction(fun() ->
|
||||
|
@ -81,6 +81,16 @@ ejson_to_record(Empty, Ref, EJSON) ->
|
||||
Constructed = ejson_to_record(Empty, EJSON),
|
||||
setelement(2, Constructed, Ref).
|
||||
|
||||
ejson_to_record_strict(Empty=#ts_user{}, EJSON) ->
|
||||
Constructed = ejson_to_record(Empty, EJSON),
|
||||
case Constructed of
|
||||
#ts_user{name = undefined} -> throw("Missing user 'name' field.");
|
||||
#ts_user{email = undefined} -> throw("Missing user 'email' field.");
|
||||
#ts_user{join_date = undefined} ->
|
||||
throw("Missing user 'join_date' field.");
|
||||
_Other -> Constructed
|
||||
end;
|
||||
|
||||
ejson_to_record_strict(Empty=#ts_timeline{}, EJSON) ->
|
||||
Constructed = ejson_to_record(Empty, EJSON),
|
||||
case Constructed of
|
||||
@ -107,22 +117,55 @@ ejson_to_record_strict(Empty, Ref, EJSON) ->
|
||||
Constructed = ejson_to_record_strict(Empty, EJSON),
|
||||
setelement(2, Constructed, Ref).
|
||||
|
||||
construct_record(Timeline=#ts_timeline{}, [{Key, Val}|Fields], ExtData) ->
|
||||
construct_record(User=#ts_user{}, [{Key, Value}|Fields], ExtData) ->
|
||||
case Key of
|
||||
created -> construct_record(
|
||||
Timeline#ts_timeline{created = decode_datetime(Val)},
|
||||
id -> construct_record(User#ts_user{username = Value},
|
||||
Fields, ExtData);
|
||||
name -> construct_record(User#ts_user{name = Value}, Fields, ExtData);
|
||||
join_date -> construct_record(
|
||||
User#ts_user{join_date = decode_datetime(Value)},
|
||||
Fields, ExtData);
|
||||
_Other ->
|
||||
ExtDataProp = ejson_to_ext_data({Key, Value}),
|
||||
construct_record(User, Fields, [ExtDataProp|ExtData])
|
||||
end;
|
||||
|
||||
construct_record(Timeline=#ts_timeline{}, [{Key, Value}|Fields], ExtData) ->
|
||||
case Key of
|
||||
user_id ->
|
||||
{_, TimelineId} = Timeline#ts_timeline.ref,
|
||||
construct_record(Timeline#ts_timeline{ref = {Value, TimelineId}},
|
||||
Fields, ExtData);
|
||||
description -> construct_record(Timeline#ts_timeline{desc = Val},
|
||||
id ->
|
||||
{Username, _} = Timeline#ts_timeline.ref,
|
||||
construct_record(Timeline#ts_timeline{ref = {Username, Value}},
|
||||
Fields, ExtData);
|
||||
created -> construct_record(
|
||||
Timeline#ts_timeline{created = decode_datetime(Value)},
|
||||
Fields, ExtData);
|
||||
description -> construct_record(Timeline#ts_timeline{desc = Value},
|
||||
Fields, ExtData);
|
||||
_Other ->
|
||||
ExtDataProp = ejson_to_ext_data({Key, Val}),
|
||||
ExtDataProp = ejson_to_ext_data({Key, Value}),
|
||||
construct_record(Timeline, Fields, [ExtDataProp|ExtData])
|
||||
end;
|
||||
|
||||
construct_record(Entry=#ts_entry{}, [{Key, Value}|Fields], ExtData) ->
|
||||
case Key of
|
||||
user_id ->
|
||||
{_, TimelineId, EntryId} = Entry#ts_entry.ref,
|
||||
construct_record(Entry#ts_entry{ref = {Value, TimelineId, EntryId}},
|
||||
Fields, ExtData);
|
||||
timeline_id ->
|
||||
{Username, _, EntryId} = Entry#ts_entry.ref,
|
||||
construct_record(Entry#ts_entry{ref = {Username, Value, EntryId}},
|
||||
Fields, ExtData);
|
||||
id ->
|
||||
{Username, TimelineId, _} = Entry#ts_entry.ref,
|
||||
construct_record(Entry#ts_entry{ref = {Username, TimelineId, Value}},
|
||||
Fields, ExtData);
|
||||
timestamp -> construct_record(
|
||||
Entry#ts_entry{timestamp = calendar:datetime_to_gregoraian_seconds(
|
||||
Entry#ts_entry{timestamp = calendar:datetime_to_gregorian_seconds(
|
||||
decode_datetime(Value))},
|
||||
Fields, ExtData);
|
||||
mark -> construct_record(Entry#ts_entry{mark=Value}, Fields, ExtData);
|
||||
|
@ -13,31 +13,34 @@ create_table(TableOpts) ->
|
||||
% expects the password in clear
|
||||
new(UR = #ts_user{}) ->
|
||||
{atomic, Result} = mnesia:transaction(fun() -> do_new(UR) end),
|
||||
Result.
|
||||
{ok, Result}.
|
||||
|
||||
new(UR = #ts_user{}, ExtData) ->
|
||||
{atomic, Result} = mnesia:transaction(fun() ->
|
||||
NewUser = do_new(UR),
|
||||
ts_common:do_set_ext_data(UR, ExtData),
|
||||
NewUser end),
|
||||
Result.
|
||||
{ok, Result}.
|
||||
|
||||
do_new(UR = #ts_user{}) ->
|
||||
case mnesia:read(ts_user, UR#ts_user.username) of
|
||||
[ExistingRecord] -> {error, {record_exists, ExistingRecord}};
|
||||
[] -> mnesia:write(hash_input_record(UR))
|
||||
[] ->
|
||||
NewRec = hash_input_record(UR),
|
||||
mnesia:write(NewRec),
|
||||
NewRec
|
||||
end.
|
||||
|
||||
update(UR = #ts_user{}) ->
|
||||
{atomic, Result} = mnesia:transaction(fun() -> do_update(UR) end),
|
||||
Result.
|
||||
{ok, Result}.
|
||||
|
||||
update(UR = #ts_user{}, ExtData) ->
|
||||
{atomic, Result} = mnesia:transaction(fun() ->
|
||||
UpdatedUser = do_update(UR),
|
||||
ts_common:do_set_ext_data(UR, ExtData),
|
||||
UpdatedUser end),
|
||||
Result.
|
||||
{ok, Result}.
|
||||
|
||||
do_update(UR = #ts_user{}) ->
|
||||
case mnesia:read(ts_user, UR#ts_user.username) of
|
||||
@ -48,7 +51,8 @@ do_update(UR = #ts_user{}) ->
|
||||
undefined -> UpdatedRecord;
|
||||
_Password -> hash_input_record(UpdatedRecord)
|
||||
end,
|
||||
mnesia:write(HashedRecord)
|
||||
mnesia:write(HashedRecord),
|
||||
HashedRecord
|
||||
end.
|
||||
|
||||
lookup(Username) ->
|
||||
|
Reference in New Issue
Block a user