diff --git a/db/test/id_counter.DCL b/db/test/id_counter.DCL new file mode 100644 index 0000000..de6df26 Binary files /dev/null and b/db/test/id_counter.DCL differ diff --git a/db/test/schema.DAT b/db/test/schema.DAT index 96b4458..0816fbd 100644 Binary files a/db/test/schema.DAT and b/db/test/schema.DAT differ diff --git a/db/test/ts_entry.DCD b/db/test/ts_entry.DCD index 09485ac..c89ba97 100644 Binary files a/db/test/ts_entry.DCD and b/db/test/ts_entry.DCD differ diff --git a/db/test/ts_timeline.DCD b/db/test/ts_timeline.DCD index c532746..1cef15a 100644 Binary files a/db/test/ts_timeline.DCD and b/db/test/ts_timeline.DCD differ diff --git a/db/test/ts_timeline.DCL b/db/test/ts_timeline.DCL new file mode 100644 index 0000000..a23140f Binary files /dev/null and b/db/test/ts_timeline.DCL differ diff --git a/db/test/ts_user.DCD b/db/test/ts_user.DCD index 1e2c376..05cef82 100644 Binary files a/db/test/ts_user.DCD and b/db/test/ts_user.DCD differ diff --git a/src/ts_api.erl b/src/ts_api.erl index 63437c4..df745cd 100644 --- a/src/ts_api.erl +++ b/src/ts_api.erl @@ -34,19 +34,18 @@ out(YArg) -> dispatch_request(YArg, _Session, []) -> make_json_404(YArg, [{see_docs, "/ts_api_doc"}]); dispatch_request(YArg, Session, [H|T]) -> - Param = path_element_to_atom(H), - case {Session, Param} of - {_, login} -> do_login(YArg); - {_, logout} -> do_logout(YArg); + case {Session, H} of + {_, "login"} -> do_login(YArg); + {_, "logout"} -> do_logout(YArg); - {not_logged_in, _} -> make_json_401(YArg); - {session_expired, _} -> make_json_401(YArg, [{error, "session expired"}]); + {"not_logged_in", _} -> make_json_401(YArg); + {"session_expired", _} -> make_json_401(YArg, [{error, "session expired"}]); - {_S, app} -> dispatch_app(YArg, Session, T); - {_S, users} -> dispatch_user(YArg, Session, T); - {_S, timelines} -> dispatch_timeline(YArg, Session, T); - {_S, entries} -> dispatch_entry(YArg, Session, T); + {_S, "app"} -> dispatch_app(YArg, Session, T); + {_S, "users"} -> dispatch_user(YArg, Session, T); + {_S, "timelines"} -> dispatch_timeline(YArg, Session, T); + {_S, "entries"} -> dispatch_entry(YArg, Session, T); {_S, _Other} -> make_json_404(YArg, [{see_docs, "/ts_api_doc/"}]) end. @@ -59,7 +58,7 @@ dispatch_app(YArg, Session, Params) -> {'GET', ["user_summary", UsernameStr]} -> case {Session#ts_api_session.username, - path_element_to_atom(UsernameStr)} of + UsernameStr} of {Username, Username} -> get_user_summary(YArg, Username); _ -> make_json_401(YArg) @@ -75,10 +74,9 @@ dispatch_app(YArg, Session, Params) -> % -------- Dispatch for /user -------- % dispatch_user(YArg, Session, []) -> - dispatch_user(YArg, Session, [atom_to_list(Session#ts_api_session.username)]); + dispatch_user(YArg, Session, [Session#ts_api_session.username]); -dispatch_user(YArg, Session, [H]) -> - Username = path_element_to_atom(H), +dispatch_user(YArg, Session, [Username]) -> HTTPMethod = (YArg#arg.req)#http_request.method, % compare to the logged-in user @@ -97,8 +95,7 @@ dispatch_user(YArg, Session, [H]) -> dispatch_timeline(YArg, _Session, []) -> make_json_404(YArg, [{see_docs, "/ts_api_doc/timelines.html"}]); -dispatch_timeline(YArg, Session, [UrlUsername|_T] = PathElements) -> - Username = path_element_to_atom(UrlUsername), +dispatch_timeline(YArg, Session, [Username|_T] = PathElements) -> case Session#ts_api_session.username of Username -> dispatch_timeline(YArg, PathElements); @@ -106,8 +103,7 @@ dispatch_timeline(YArg, Session, [UrlUsername|_T] = PathElements) -> end. % just username, list timelines -dispatch_timeline(YArg, [UrlUsername]) -> - Username = path_element_to_atom(UrlUsername), +dispatch_timeline(YArg, [Username]) -> HTTPMethod = (YArg#arg.req)#http_request.method, case HTTPMethod of @@ -115,9 +111,7 @@ dispatch_timeline(YArg, [UrlUsername]) -> _Other -> make_json_405(YArg, [{see_docs, "/ts_api_doc/timelines.html"}]) end; -dispatch_timeline(YArg, [UrlUsername, UrlTimelineId]) -> - Username = path_element_to_atom(UrlUsername), - TimelineId = path_element_to_atom(UrlTimelineId), +dispatch_timeline(YArg, [Username, TimelineId]) -> HTTPMethod = (YArg#arg.req)#http_request.method, case HTTPMethod of @@ -136,17 +130,14 @@ dispatch_timeline(YArg, _Other) -> dispatch_entry(YArg, _Session, []) -> make_json_404(YArg, [{see_docs, "/ts_aip_doc/entries.html"}]); -dispatch_entry(YArg, Session, [UrlUsername|_T] = PathElements) -> - Username = path_element_to_atom(UrlUsername), +dispatch_entry(YArg, Session, [Username|_T] = PathElements) -> case Session#ts_api_session.username of Username -> dispatch_entry(YArg, PathElements); _Other -> make_json_404(YArg, [{see_docs, "/ts_api_doc/entries.html"}]) end. -dispatch_entry(YArg, [UrlUsername, UrlTimelineId]) -> - Username = path_element_to_atom(UrlUsername), - TimelineId = path_element_to_atom(UrlTimelineId), +dispatch_entry(YArg, [Username, TimelineId]) -> HTTPMethod = (YArg#arg.req)#http_request.method, case HTTPMethod of @@ -155,9 +146,7 @@ dispatch_entry(YArg, [UrlUsername, UrlTimelineId]) -> _Other -> make_json_405(YArg, [{see_docs, "/ts_api_doc/entries.html"}]) end; -dispatch_entry(YArg, [UrlUsername, UrlTimelineId, UrlEntryId]) -> - Username = path_element_to_atom(UrlUsername), - TimelineId = path_element_to_atom(UrlTimelineId), +dispatch_entry(YArg, [Username, TimelineId, UrlEntryId]) -> EntryId = list_to_integer(UrlEntryId), % TODO: catch non-numbers HTTPMethod = (YArg#arg.req)#http_request.method, @@ -184,8 +173,7 @@ do_login(YArg) -> lists:keyfind(password, 1, Fields)} of % username and password found - {{username, UnameField}, {password, Password}} -> - Username = list_to_atom(UnameField), + {{username, Username}, {password, Password}} -> % check the uname, password case ts_user:check_credentials(Username, Password) of @@ -281,7 +269,11 @@ post_timeline(YArg, Username, TimelineId) -> EJSON = parse_json_body(YArg), % parse into a Timeline record - TR = ts_json:ejson_to_record(#ts_timeline{}, EJSON), + TR = try ts_json:ejson_to_record(#ts_timeline{}, EJSON) + catch _:InputError -> + error_logger:error_report("Bad input: ~p", [InputError]), + throw(make_json_400(YArg)) + end, % set username and timeline id NewRecord = TR#ts_timeline{ref = {Username, TimelineId}}, @@ -309,12 +301,16 @@ put_timeline(YArg, Username, TimelineId) -> %{struct, Fields} = EJSON, % parse into a timeline record - TR = ts_json:ejson_to_record(#ts_timeline{}, EJSON), + TR = try ts_json:ejson_to_record(#ts_timeline{}, EJSON) + catch _:InputError -> + error_logger:error_report("Bad input: ~p", [InputError]), + throw(make_json_400(YArg)) + end, % not supported right now, would require changing all the entry keys % check to see if they are changing the timeline id %NewTimelineId = case lists:keyfind(1, timeline_id, Fields) of - % {timeline_id, Field} -> list_to_atom(Field); + % {timeline_id, Field} -> Field; % false -> TimelineId end, % set username and timeline id @@ -428,7 +424,11 @@ post_entry(YArg, Username, TimelineId) -> EJSON = parse_json_body(YArg), % parse into ts_entry record - ER = ts_json:ejson_to_record(#ts_entry{}, EJSON), + ER = try ts_json:ejson_to_record(#ts_entry{}, EJSON) + catch _:InputError -> + error_logger:error_report("Bad input: ~p", [InputError]), + throw(make_json_400(YArg)) + end, % set username and timeline id NewRecord = ER#ts_entry{ref = {Username, TimelineId, undef}}, @@ -455,7 +455,11 @@ put_entry(YArg, Username, TimelineId, EntryId) -> EJSON = parse_json_body(YArg), % parse into ts_entry record - ER = ts_json:ejson_to_record(#ts_entry{}, EJSON), + ER = try ts_json:ejson_to_record(#ts_entry{}, EJSON) + catch _:InputError -> + error_logger:error_report("Bad input: ~p", [InputError]), + throw(make_json_400(YArg)) + end, % set uername, timeline id, and entry id NewRecord = ER#ts_entry{ref = {Username, TimelineId, EntryId}}, @@ -492,10 +496,6 @@ delete_entry(YArg, Username, TimelineId, EntryId) -> % ======== UTIL METHODS ======== % % ============================== % -%% Convert one path element to an atom. -path_element_to_atom(PE) -> - list_to_atom(re:replace(PE, "\\s", "_", [{return, list}])). - parse_json_body(YArg) -> case catch json:decode([], binary_to_list(YArg#arg.clidata)) of {done, {ok, EJSON}, _} -> EJSON; @@ -547,19 +547,19 @@ 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}]; + false -> Fields ++ [{status, "method not allowed"}]; _Else -> Fields end, % add the path they requested - F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}], + % F2 = F1 ++ [{path, io_lib:format("~s", [(YArg#arg.req)#http_request.path])}], - [{status, 405}, {content, "application/json", json:encode({struct, F2})}]. + [{status, 405}, {content, "application/json", json:encode({struct, F1})}]. make_json_500(_YArg, Error) -> EJSON = {struct, [ {status, "internal server error"}, - {error, io_lib:format("~p", [Error])}]}, + {error, io_lib:format("~s", [Error])}]}, [{status, 500}, {content, "application/json", json:encode(EJSON)}]. make_json_500(_YArg) -> diff --git a/src/ts_db_records.hrl b/src/ts_db_records.hrl index 2cfc828..2d94a7b 100644 --- a/src/ts_db_records.hrl +++ b/src/ts_db_records.hrl @@ -4,8 +4,8 @@ pwd_salt, name, email, - join_date%, -% ext_data % other extensible data + join_date, + ext_data = [] % other extensible data }). % ts_user.ext_data can be: diff --git a/src/ts_json.erl b/src/ts_json.erl index 0f10a79..4079353 100644 --- a/src/ts_json.erl +++ b/src/ts_json.erl @@ -7,10 +7,11 @@ encode_record(Record) -> lists:flatten(json:encode(record_to_ejson(Record))). record_to_ejson(Record=#ts_user{}) -> {struct, [ - {id, atom_to_list(Record#ts_user.username)}, + {id, Record#ts_user.username}, {name, Record#ts_user.name}, {email, Record#ts_user.email}, - {join_date, encode_datetime(Record#ts_user.join_date)}]}; + {join_date, encode_datetime(Record#ts_user.join_date)}, + {ext_data, Record#ts_user.ext_data}]}; record_to_ejson(Record=#ts_timeline{}) -> % pull out the username and timeline id @@ -18,8 +19,8 @@ record_to_ejson(Record=#ts_timeline{}) -> % create the EJSON struct {struct, [ - {user_id, atom_to_list(Username)}, - {id, atom_to_list(TimelineId)}, + {user_id, Username}, + {id, TimelineId}, {created, encode_datetime(Record#ts_timeline.created)}, {description, Record#ts_timeline.desc}]}; @@ -32,15 +33,15 @@ record_to_ejson(Record=#ts_entry{}) -> % create the EJSON struct {struct, [ - {user_id, atom_to_list(Username)}, - {timeline_id, atom_to_list(TimelineId)}, + {user_id, Username}, + {timeline_id, TimelineId}, {id, EntryId}, {timestamp, encode_datetime(DateTime)}, {mark, Record#ts_entry.mark}, {notes, Record#ts_entry.notes}]}. encode_datetime({{Year, Month, Day}, {Hour, Minute, Second}}) -> - lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B~3.10.0BZ", + lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~3.10.0BZ", [Year, Month, Day, Hour, Minute, Second, 000])). ejson_to_record(_Empty=#ts_timeline{}, EJSON) ->