diff --git a/src/ts_api.erl b/src/ts_api.erl index 0dee7e0..2170ee4 100644 --- a/src/ts_api.erl +++ b/src/ts_api.erl @@ -130,12 +130,12 @@ put_user(YArg) -> % will not create, record exists {error, {record_exists, ExistingRecord}} -> - JSONReturn = json:encode({struct, [ + JSONResponse = json:encode({struct, [ {status, "username taken"}, {see_docs, "/ts_api_doc/user#PUT"} ]}), - [{status, 409}, {content, "application/json", JSONReturn}]; + [{status, 409}, {content, "application/json", JSONResponse}]; _Error -> make_json_500(YArg) end. @@ -164,11 +164,14 @@ list_timelines(YArg, Username) -> % read or default the Start Start = case lists:keyfind(start, 1, PostData) of - {start, StartVal} -> StartVal; false -> 0 end, + {start, StartVal} -> list_to_integer(StartVal); + false -> 0 + end, % read or default the Length Length = case lists:keyfind(length, 1, PostData) of - {length, LengthVal} -> erlang:min(LengthVal, 50); + {length, LengthVal} -> + erlang:min(list_to_integer(LengthVal), 50); false -> 50 end, @@ -190,7 +193,7 @@ get_timeline(YArg, Username, TimelineId) -> % look for timeline case ts_timeline:lookup(Username, TimelineId) of % no such timeline, return 404 - no_record -> make_json_404(YArg); + no_record -> make_json_404(YArg, [{status, "no_such_timeline"}]); % return the timeline data Timeline -> make_json_200(YArg, Timeline) end. @@ -212,13 +215,13 @@ put_timeline(YArg, Username) -> {error, {record_exists, ExistingRecord}} -> EJSONRec = ts_json:record_to_ejson(ExistingRecord), - JSONReturn = json:encode({struct, [ + JSONResponse = json:encode({struct, [ {status, "ignored"}, {timeline, EJSONRec}, - {see_docs, "/ts_api_doc/timeline#PUT"} + {see_docs, "/ts_api_doc/timelines#PUT"} ]}), - {content, "application/json", JSONReturn}; + {content, "application/json", JSONResponse}; _Error -> make_json_500(YArg) end. @@ -234,48 +237,156 @@ post_timeline(YArg, Username, TimelineId) -> ok -> make_json_200(YArg, NewRecord); no_record -> make_json_404(YArg, - [{status, "no_record"}, {see_docs, "/ts_api_doc/timeline#PSOT"}]); + [{status, "no_such_timeline"}, + {see_docs, "/ts_api_doc/timelines#POST"}]); _Error -> make_json_500(YArg) end. delete_timeline(YArg, Username, TimelineId) -> {status, 405}. -list_entries(YArg, Username, Timeline) -> +list_entries(YArg, Username, TimelineId) -> % pull out the POST data PostData = yaws_api:parse_post(YArg), % first determine if we are listing by date - case lists:keyfind(byDate, 1, PostData) of - {byDate, "true"} -> - _Other - % read or default the Start - Start = case lists:keyfind(start, 1, PostData) of - {start, StartVal} -> StartVal; false -> 0 end, + case {ts_timeline:lookup(Username, TimelineId), + lists:keyfind(byDate, 1, PostData)} of - % read or default the Length - Length = case lists:keyfind(length, 1, PostData) of - {length, LengthVal} -> erlang:min(LengthVal, 500); - false -> 50 - end, + {no_record, _ByDateField} -> make_json_404( + [{status, "no_such_timeline"}, + {see_docs, "/ts_api_doc/entries#LIST"}]); - % read or default the sort order - SortOrder = case lists:keyfind(order, 1, PostData) of - {order, "asc"} -> asc; - {order, "desc"} -> desc; - _Other -> asc - end, + % listing by date range + {Timeline, {byDate, "true"}} -> - Entries = case SortOrder of - asc -> ts_entry:list_asc({Username, Timeline}, Start, Length); - desc -> ts_entry:list_desc({Username, Timeline}, Start, Length) + % look for the start date; default to the beginning of the timeline + StartDate = case lists:keyfind(startDate, 1, PostData) of + % TODO: error handling if the date is badly formatted + {startDate, StartDateVal} -> ts_json:decode_date(StartDateVal); + false -> Timeline#ts_timeline.created + end, + + % look for end date; default to right now + EndDate = case lists:keyfind(endDate, 1, PostData) of + % TODO: error handling if the date is badly formatted + {endDate, EndDateVal} -> ts_json:decode_date(EndDateVal); + false -> calendar:now_to_universal_time(erlang:now()) + end, + + % read sort order and list entries + Entries = case lists:keyfind(order, 1, PostData) -> + % descending sort order + {order, "desc"} -> ts_entry:list_desc( + {Username, TimelineId}, StartDate, EndDate); + + % ascending order--{other, asc}--and default + _Other -> ts_entry:list_asc( + {Username, TimelineId}, StartDate, EndDate) + end, + + EJSONEntries = {array, lists:map( + fun ts_json:record_to_ejson/1, Entries)}, + + JSONResponse = json:encode({struct, [ + {status, "ok"}, + {entries, EJSONEntries}]}), + + {content, "application/json", JSONResponse; + + % listing by table position + _Other -> + + % read or default the Start + Start = case lists:keyfind(start, 1, PostData) of + {start, StartVal} -> list_to_integer(StartVal); + false -> 0 + end, + + % read or default the Length + Length = case lists:keyfind(length, 1, PostData) of + {length, LengthVal} -> + erlang:min(list_to_integer(LengthVal), 500); + false -> 50 + end, + + % read sort order and list entries + Entries = case lists:keyfind(order, 1, PostData) of + {order, "desc"} -> ts_entry:list_desc( + {Username, TimelineId}, Start, Length); + + _Other -> ts_entry:list_asc( + {Username, TimelineId}, Start, Length) + end, + + EJSONEntries = {array, lists:map( + fun ts_json:record_to_ejson/1, Entries)}, + + JSONResponse = json:encode({struct, [ + {status, "ok"}, + {entries, EJSONEntries}]}), + + {content, "application/json", JSONResponse end. -get_entry(YArg, Username, TimelineId, EntryId) -> todo. +get_entry(YArg, Username, TimelineId, EntryId) -> + case ts_entry:lookup(Username, TimelineId, EntryId) of + % no such entry + no_record -> make_json_404(YArg, [{status, "no_such_entry"}]); + % return the entry data + Entry -> make_json_200(YArg, Entry) + end. -put_entry(YArg, Username, TimelineId) -> todo. +put_entry(YArg, Username, TimelineId) -> -post_entry(YArg, Username, TimelineId, EntryId) -> todo. + % parse the request body + {done, {ok, EJSON}, _} = json:decode([], binary_to_list(YArg#arg.clidata)), + + % pull out the entry data + {struct, Fields} = EJSON, + EJSONEntry = lists:findkey(entry, 1, Fields), % TODO: check for errors + + % parse into ts_entry record + NewRecord = ts_json:ejson_to_record(#ts_entry{}, EJSONEntry), + + case ts_entry:new(NewRecord) of + % record created + ok -> [{status, 201}, make_json_200(YArg, NewRecord)]; + + % will not create, record exists + {error, {record_exists, ExistingRecord}} -> + EJSONRec = ts_json:record_to_ejson(ExistingRecord), + JSONResponse = json:encode({struct, [ + {status, "ignored"}, + {entry, EJSONRec}, + {see_docs, "/ts_api_doc/entries#PUT"} + ]}), + + {content, "application/json", JSONResposne}; + + _Error -> make_json_500(YArg) + end. + +post_entry(YArg, Username, TimelineId, EntryId) -> + + % parse the POST data + {done, {ok, EJSON}, _} = json:decode([], binary_to_list(YArg#arg.clidata)), + + % pull out the entry data + {struct, Fields} = EJSON, + EJSONEntry = lists:findkey(entry, 1, Fields), % TODO: error handling + + % parse into ts_entry record + NewRecord = ts_json:ejson_to_record(#ts_entry{}, EJSONEntry), + + case ts_entry:update(NewRecord) of + ok -> make_json_200(YArg, NewRecord); + + no_record -> make_json_404(YArg, + [{status, "no_such_entry"}, {see_docs, "/ts_api_doc/entries#POST"}]); + + _Error -> make_json_500(YArg) + end. delete_entry(YArg, Username, TimelineId, EntryId) -> todo. @@ -290,12 +401,17 @@ path_element_to_atom(PE) -> %% Create a JSON 200 response. make_json_200(YArg, Record) -> EJSONRecord = ts_json:record_to_ejson(Record), - JSONReturn = json:encode({struct, [ + Tag = case element(1, Record) of + ts_user -> user; + ts_timeilne -> timeline; + ts_entry -> entry + end, + JSONResponse = json:encode({struct, [ {status, "ok"}, - {element(1, Record), EJSONRecord} + {Tag, EJSONRecord} ]}), - {content, "application/json", JSONReturn}. + {content, "application/json", JSONResponse}. %% Create a JSON 404 response. make_json_404(YArg) -> make_json_404(YArg, []).