Additional parts of the API, DB layer.

Implemented additional API functions:
    * dispatch_timeline/4
    * dispatch_event_by_id/4
    * get_timeline/3
    * put_timeline/3
    * post_timeline/3
    * make_json_404/2, make_json_405/2, make_json_500/1
Implemented ts_timeline:lookup/2
Implemented ts_entry:lookup/2
This commit is contained in:
Jonathan Bernard
2011-01-30 08:28:56 -06:00
parent b690326cf4
commit 309d6915fc
7 changed files with 240 additions and 20 deletions

View File

@ -21,7 +21,7 @@ out(YArg) ->
%% Entry point to the TimeStamper API dispatch system
dispatch_request(_YArg, []) ->
% no arguments: URL is /ts_api or /ts_api/, shoe API docs
% no arguments: URL is /ts_api or /ts_api/, show API docs
{page, "/ts_api_doc/index.html"};
dispatch_request(YArg, [H|T]) ->
@ -34,26 +34,122 @@ dispatch_request(YArg, [H|T]) ->
dispatch_timeline(YArg, Username, Timeline, Params)
end.
% no events, show timeline pages
% no entries, show timeline pages
dispatch_timeline(YArg, Username, Timeline, []) ->
Req = YArg#arg.arg,
HTTPMethod = Req#http_request.method,
case HTTPMethod of
'GET' -> get_timeline(YArg, Username, Timeline);
'PUT' -> put_timeline(YArg, Username, Timeline);
'POST' -> post_timeline(YArg, Username, Timeline);
'DELETE' -> delete_timeline(YArg, Username, Timeline)
'GET' -> get_timeline(YArg, Username, Timeline);
'PUT' -> put_timeline(YArg, Username, Timeline);
'POST' -> post_timeline(YArg, Username, Timeline);
'DELETE' -> delete_timeline(YArg, Username, Timeline);
_Other -> make_json_405(YArg)
end;
dispatch_timeline(YArg, Username, Timeline, [H|T]) ->
Param = path_element_to_atom(H),
case Param of ->
list -> list_entries(YArg, Username, Timeline);
by_id -> dispatch_entry_by_id(YArg, Username, Timeline, T);
by_date -> dispatch_entry_by_date(YArg, Username, Timeline, T);
_Other -> make_json_404(YArg)
end.
dispatch_entry_by_id(YArg, Username, Timeline, []) ->
make_json_404(YArg, [{note, "An entry id is expected in the URL."}];
dispatch_entry_by_id(YArg, Username, Timeline, [H|T]) ->
EventId = list_to_integer(H), % TODO: guard against bad input
get_entry_by_id(Username, Timeline, EventId).
dispatch_entry_by_date(YArg, Username, Timeline, Params) -> todo.
% ============================== %
% ======== IMPLEMENTATION ====== %
% ============================== %
get_timeline(YArg, Username, TimelineId) ->
Timeline = mnesia:dirty_read(ts_timeline, {Username, TimelineId}).
case ts_timeline:lookup(Username, TimelineId) of
no_record -> make_json_404(YArg);
Timeline ->
EJSONTimeline = ts_json:record_to_ejson(Timeline),
JSONReturn = json:encode({struct, [
{status, "ok"},
{timeline, EJSONTimeline}
]}),
{content, "application/json", JSONReturn}
end.
put_timeline(YArg, Username, TimelineId) ->
% parse the request body
{done, {ok, EJSON}, _} = json:decode([], binary_to_list(YArg#arg.clidata)),
% parse into a Timeline record
EmptyTimeline = #ts_timeline{ref = {Username, TimelineId}},
NewRecord = ts_json:ejson_to_record(EmptyTimeline, EJSON),
% insert into the database
case ts_timeline:new(NewRecord) of
% record created
ok ->
EJSONRec = ts_json:record_to_ejson(NewRecord)
JSONReturn = json:encode({struct, [
{status, "ok"},
{record, EJSONRec}
]}),
% return the new record
[{status, 201}, {content, "application/json", JSONReturn}];
% will not create, record exists
{error, {record_exists, ExistingRecord}} ->
EJSONRec = ts_json:record_to_ejson(ExistingRecord),
JSONReturn = json:encode({struct, [
{status, "ignored"},
{record, EJSONRec},
{see_docs, "/ts_api_doc/timeline#PUT"}
]}),
{content, "application/json", JSONReturn};
_Error -> make_json_500(YArg)
end.
post_timeline(YArg, Username, TimelineId) ->
% parse the POST data
{done, {ok, EJSON}, _} = json:decode([], binary_to_list(YArg#arg.clidata)),
% create the timeline record
EmptyTimeline = #ts_timeline{ref = {Username, TimelineId}},
NewRecord = ts_json:ejson_to_record(EmptyTimeline, EJSON),
case ts_timeline:update(NewRecord) of
ok ->
EJSONRec = ts_json:record_to_ejson(NewRecord),
JSONReturn = json:encode({struct, [
{status, "updated"},
{record, EJSONRec}
]}),
{content, "application/json", JSONReturn};
no_record -> make_json_404(YArg, [{status, "no_record"},
{see_docs, "/ts_api_doc/timeline#PSOT"}]);
_Error -> make_json_500(YArg)
end.
delete_timeline(YArg, Username, TimelineId) -> todo.
% ============================== %
% ======== UTIL METHODS ======== %
@ -62,3 +158,36 @@ get_timeline(YArg, Username, TimelineId) ->
%% Convert one path element to an atom.
path_element_to_atom(PE) ->
list_to_atom(re:replace(PE, "\\s", "_", [{return, list}])).
%% Create a JSON 404 response.
make_json_404(YArg) -> make_json_404(YArg, []).
make_json_404(YArg, Fields) ->
% add default status if not provided
F1 = case lists:keyfind(status, 1, Fields) of
false -> Fields ++ [{status, not_found}];
_Else -> Fields
end,
% add the path they requested
F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}],
[{status, 404}, {content, "application/json", json:encode({struct, Fields})}].
make_json_405(YArg) -> make_json_405(YArg, []).
make_json_405(YArg, Fields) ->
% add default status if not provided
F1 = case lists:keyfind(status, 1, Fields) of
false -> Fields ++ [{status, method_not_allowed}];
_Else -> Fields
end,
% add the path they requested
F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}],
[{status, 405}, {content, "application/json", json:encode({struct, Fields})}].
make_json_500(_YArg) ->
EJSON = {struct, [
{status, "error"},
{error, "Internal Server Error"}]},
[{status, 500}, {content, "application/json", json:encode(EJSON)}].