* Changed the cookie Path value to allow the cookie to be reused for the domain, not just ths `/ts_api` path. This allows the user to refresh the page and reuse their existing session as long as it is not stale. * Fixed a bug in the `ts_json:ejson_to_record_strict/2` function. It was expecting a record out of `ts_json:ejson_to_record/2` but that function returns a tuple with the record and extended data. Because of the way `ejson_to_record_strict` uses case statements to check for specific values it was still passing the parsed record and data through, but all the checks were being bypassed. * Fixed bugs in the `index.yaws` bootstrap code for the case where the user already has a valid session. * Added `urlRoot` functions to the Backbone model definitions. * Changed the behavior of the new entry creation method. We were trying to fetch just updated attributes from the server for the model we created, but we were pulling all the entries due to the URL backbone was using. This led to the new client-side model having all the previous entry models as attributes. Ideally we would fix the fetch so that only the one model is requested from the server, but we run into a catch-22 because the lookup id is not know by the client as it is generated on the server-side. For now I have changed this behavior so that we still pull all entries, but we pull them into the collection. The collection is then smart enough to update the entries that have changed (namely the new one). The server returns the newly created entry attributes in response to the POST request that the client makes initially, so when I have more time to work on this I plan to do away with the fetch after create, and just pull in the data from the server's response. * Changed formatting.
609 lines
21 KiB
Erlang
609 lines
21 KiB
Erlang
-module(ts_api).
|
|
-compile(export_all).
|
|
|
|
-include("ts_db_records.hrl").
|
|
-include("yaws_api.hrl").
|
|
|
|
out(YArg) ->
|
|
% retreive the session data
|
|
Session = ts_api_session:get_session(YArg),
|
|
|
|
%get the app mod data
|
|
PathString = YArg#arg.appmoddata,
|
|
|
|
% split the path
|
|
PathElements = case PathString of
|
|
undefined -> []; %handle no end slash: /ts_api
|
|
_Any -> string:tokens(PathString, "/")
|
|
end,
|
|
|
|
% process the request
|
|
case catch dispatch_request(YArg, Session, PathElements) of
|
|
{'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.
|
|
|
|
% ================================== %
|
|
% ======== DISPATCH METHODS ======== %
|
|
% ================================== %
|
|
|
|
%% Entry point to the TimeStamper API dispatch system
|
|
dispatch_request(YArg, _Session, []) -> make_json_404(YArg, [{"see_docs", "/ts_api_doc"}]);
|
|
|
|
dispatch_request(YArg, Session, [H|T]) ->
|
|
|
|
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"}]);
|
|
|
|
{_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.
|
|
|
|
% -------- Dispatch for /app -------- %
|
|
|
|
dispatch_app(YArg, Session, Params) ->
|
|
HTTPMethod = (YArg#arg.req)#http_request.method,
|
|
|
|
case {HTTPMethod, Params} of
|
|
|
|
{'OPTIONS', ["user_summary", _]} -> make_CORS_options(YArg, "GET");
|
|
|
|
{'GET', ["user_summary", UsernameStr]} ->
|
|
case {Session#ts_api_session.username,
|
|
UsernameStr} of
|
|
|
|
{Username, Username} -> get_user_summary(YArg, Username);
|
|
_ -> make_json_401(YArg)
|
|
end;
|
|
|
|
{_BadMethod, ["user_summary", _UsernameStr]} ->
|
|
make_json_405(YArg, [{"see_docs", "/ts_api_docs/app.html"}]);
|
|
|
|
_Other -> make_json_404(YArg, [{"see_docs", "/ts_api_docs/app.html"}])
|
|
|
|
end.
|
|
|
|
% -------- Dispatch for /user -------- %
|
|
|
|
dispatch_user(YArg, Session, []) ->
|
|
dispatch_user(YArg, Session, [Session#ts_api_session.username]);
|
|
|
|
dispatch_user(YArg, Session, [Username]) ->
|
|
HTTPMethod = (YArg#arg.req)#http_request.method,
|
|
|
|
% compare to the logged-in user
|
|
case {HTTPMethod, Session#ts_api_session.username} of
|
|
|
|
{'OPTIONS', Username} -> make_CORS_options(YArg, "GET, PUT");
|
|
{'GET', Username} -> get_user(YArg, Username);
|
|
{'PUT', Username} -> put_user(YArg, Username);
|
|
|
|
{_BadMethod, Username} ->
|
|
make_json_405(YArg, [{"see_docs", "/ts_api_doc/users.html"}]);
|
|
|
|
_Other -> make_json_401(YArg, [{"see_docs", "/ts_api_doc/users.html"}])
|
|
end.
|
|
|
|
% -------- Dispatch for /timeline -------- %
|
|
|
|
dispatch_timeline(YArg, _Session, []) ->
|
|
make_json_404(YArg, [{"see_docs", "/ts_api_doc/timelines.html"}]);
|
|
|
|
dispatch_timeline(YArg, Session, [Username|_T] = PathElements) ->
|
|
|
|
case Session#ts_api_session.username of
|
|
Username -> dispatch_timeline(YArg, PathElements);
|
|
_Other -> make_json_404(YArg, [{"see_docs", "/ts_api_doc/users.html"}])
|
|
end.
|
|
|
|
% just username, list timelines
|
|
dispatch_timeline(YArg, [Username]) ->
|
|
HTTPMethod = (YArg#arg.req)#http_request.method,
|
|
|
|
case HTTPMethod of
|
|
'OPTIONS' -> make_CORS_options(YArg, "GET");
|
|
'GET' -> list_timelines(YArg, Username);
|
|
_Other -> make_json_405(YArg, [{"see_docs", "/ts_api_doc/timelines.html"}])
|
|
end;
|
|
|
|
dispatch_timeline(YArg, [Username, TimelineId]) ->
|
|
HTTPMethod = (YArg#arg.req)#http_request.method,
|
|
|
|
case HTTPMethod of
|
|
'OPTIONS'-> make_CORS_options(YArg, "GET, PUT, DELETE");
|
|
'GET' -> get_timeline(YArg, Username, TimelineId);
|
|
'PUT' -> put_timeline(YArg, Username, TimelineId);
|
|
'DELETE' -> delete_timeline(YArg, Username, TimelineId);
|
|
_Other -> make_json_405(YArg, [{"see_docs", "/ts_api_doc/timelines.html"}])
|
|
end;
|
|
|
|
dispatch_timeline(YArg, _Other) ->
|
|
make_json_404(YArg, [{"see_docs", "/ts_api_doc/timelines.html"}]).
|
|
|
|
% -------- Dispatch for /entry -------- %
|
|
|
|
dispatch_entry(YArg, _Session, []) ->
|
|
make_json_404(YArg, [{"see_docs", "/ts_aip_doc/entries.html"}]);
|
|
|
|
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, [Username, TimelineId]) ->
|
|
HTTPMethod = (YArg#arg.req)#http_request.method,
|
|
|
|
case HTTPMethod of
|
|
'OPTIONS' -> make_CORS_options(YArg, "GET, POST");
|
|
'GET' -> list_entries(YArg, Username, TimelineId);
|
|
'POST' -> post_entry(YArg, Username, TimelineId);
|
|
_Other -> make_json_405(YArg, [{"see_docs", "/ts_api_doc/entries.html"}])
|
|
end;
|
|
|
|
dispatch_entry(YArg, [Username, TimelineId, UrlEntryId]) ->
|
|
EntryId = list_to_integer(UrlEntryId), % TODO: catch non-numbers
|
|
HTTPMethod = (YArg#arg.req)#http_request.method,
|
|
|
|
case HTTPMethod of
|
|
'OPTIONS'-> make_CORS_options(YArg, "GET, PUT, DELETE");
|
|
'GET' -> get_entry(YArg, Username, TimelineId, EntryId);
|
|
'PUT' -> put_entry(YArg, Username, TimelineId, EntryId);
|
|
'DELETE' -> delete_entry(YArg, Username, TimelineId, EntryId);
|
|
_Other -> make_json_405(YArg, [{"see_docs", "/ts_api_doc/entries.html"}])
|
|
end;
|
|
|
|
dispatch_entry(YArg, _Other) ->
|
|
make_json_404(YArg, [{"see_docs", "/ts_api_doc/entries.html"}]).
|
|
|
|
% ============================== %
|
|
% ======== IMPLEMENTATION ====== %
|
|
% ============================== %
|
|
|
|
do_login(YArg) ->
|
|
EJSON = parse_json_body(YArg),
|
|
|
|
{struct, Fields} = EJSON,
|
|
|
|
case {lists:keyfind(username, 1, Fields),
|
|
lists:keyfind(password, 1, Fields)} of
|
|
|
|
% username and password found
|
|
{{username, Username}, {password, Password}} ->
|
|
|
|
% check the uname, password
|
|
case ts_user:check_credentials(Username, Password) of
|
|
% they are good
|
|
true ->
|
|
{CookieVal, _Session} = ts_api_session:new(Username),
|
|
|
|
[{header, {set_cookie, io_lib:format(
|
|
"ts_api_session=~s; Path=/",
|
|
[CookieVal])}},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]},
|
|
{content, "application/json",
|
|
json:encode({struct, [{"status", "ok"}]})}];
|
|
|
|
% they are not good
|
|
false -> make_json_401(YArg, [{"error",
|
|
"bad username/password combination"}])
|
|
end;
|
|
|
|
_Other -> make_json_400(YArg, [{"see_docs", "/ts_api_doc/login.html"}])
|
|
end.
|
|
|
|
do_logout(YArg) ->
|
|
Cookie = (YArg#arg.headers)#headers.cookie,
|
|
CookieVal = yaws_api:find_cookie_val("ts_api_session", Cookie),
|
|
ts_api_session:logout(CookieVal),
|
|
[{status, 200},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
get_user_summary(YArg, Username) ->
|
|
% find user record
|
|
case ts_user:lookup(Username) of
|
|
|
|
% no user record, barf
|
|
no_record -> make_json_404(YArg);
|
|
|
|
% found user record, let us build the return
|
|
User ->
|
|
|
|
% get user extended data properties
|
|
UserExtData = ts_ext_data:get_properties(User),
|
|
% convert to intermediate JSON form
|
|
EJSONUser = ts_json:record_to_ejson(User, UserExtData),
|
|
|
|
% get the user's timelins
|
|
Timelines = ts_timeline:list(Username, 0, 100),
|
|
|
|
% get each timeline's extended data and convert to EJSON
|
|
EJSONTimelines = {array,
|
|
lists:map(
|
|
fun(Timeline) ->
|
|
ts_json:record_to_ejson(Timeline,
|
|
ts_ext_data:get_properties(Timeline))
|
|
end,
|
|
Timelines)},
|
|
|
|
% write response out
|
|
make_json_200(YArg, {struct,
|
|
[{user, EJSONUser},
|
|
{timelines, EJSONTimelines}
|
|
]})
|
|
end.
|
|
|
|
get_user(YArg, Username) ->
|
|
% find the user record
|
|
case ts_user:lookup(Username) of
|
|
% no such user, barf
|
|
no_record -> make_json_404(YArg);
|
|
% found, return a 200 with the record
|
|
User -> make_json_200_record(YArg, User)
|
|
end.
|
|
|
|
put_user(YArg, Username) ->
|
|
|
|
% parse the POST data
|
|
EJSON = parse_json_body(YArg),
|
|
|
|
{UR, ExtData} =
|
|
try ts_json: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_record(YArg, UpdatedRec).
|
|
|
|
list_timelines(YArg, Username) ->
|
|
% pull out the POST data
|
|
QueryData = yaws_api:parse_query(YArg),
|
|
|
|
% read or default the Start
|
|
Start = case lists:keyfind("start", 1, QueryData) of
|
|
{"start", StartVal} -> list_to_integer(StartVal);
|
|
false -> 0
|
|
end,
|
|
|
|
% read or default the Length
|
|
Length = case lists:keyfind(length, 1, QueryData) of
|
|
{"length", LengthVal} ->
|
|
erlang:min(list_to_integer(LengthVal), 50);
|
|
false -> 50
|
|
end,
|
|
|
|
% list the timelines from the database
|
|
Timelines = ts_timeline:list(Username, Start, Length),
|
|
|
|
% convert them all to their EJSON form, adding in extended data for each
|
|
EJSONTimelines = {array, lists:map(
|
|
fun (Timeline) ->
|
|
ts_json:record_to_ejson(Timeline,
|
|
ts_ext_data:get_properties(Timeline))
|
|
end,
|
|
Timelines)},
|
|
|
|
% return response
|
|
make_json_200(YArg, EJSONTimelines).
|
|
|
|
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, [{"error", "no such timeline"}]);
|
|
% return the timeline data
|
|
Timeline -> make_json_200_record(YArg, Timeline)
|
|
end.
|
|
|
|
put_timeline(YArg, Username, TimelineId) ->
|
|
% parse the POST data
|
|
EJSON = parse_json_body(YArg),
|
|
%{struct, Fields} = EJSON,
|
|
|
|
% parse into a timeline record
|
|
{TR, ExtData} =
|
|
try ts_json:ejson_to_record_strict(
|
|
#ts_timeline{ref={Username, TimelineId}}, EJSON)
|
|
% 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}]))
|
|
end,
|
|
|
|
% write the changes.
|
|
ts_timeline:write(TR, ExtData),
|
|
|
|
% return a 200
|
|
make_json_200_record(YArg, TR).
|
|
|
|
delete_timeline(_YArg, _Username, _TimelineId) -> {status, 405}.
|
|
|
|
list_entries(YArg, Username, TimelineId) ->
|
|
% pull out the POST data
|
|
QueryData = yaws_api:parse_query(YArg),
|
|
|
|
% first determine if we are listing by date
|
|
case {ts_timeline:lookup(Username, TimelineId),
|
|
lists:keyfind("byDate", 1, QueryData)} of
|
|
|
|
{no_record, _ByDateField} -> make_json_404(
|
|
[{"error", "no such timeline"},
|
|
{"see_docs", "/ts_api_doc/entries.html#LIST"}]);
|
|
|
|
% listing by date range
|
|
{Timeline, {"byDate", "true"}} ->
|
|
|
|
% look for the start date; default to the beginning of the timeline
|
|
StartDate = case lists:keyfind("startDate", 1, QueryData) of
|
|
% TODO: error handling if the date is badly formatted
|
|
{"startDate", StartDateVal} -> ts_json:decode_datetime(StartDateVal);
|
|
false -> Timeline#ts_timeline.created
|
|
end,
|
|
|
|
% look for end date; default to right now
|
|
EndDate = case lists:keyfind("endDate", 1, QueryData) of
|
|
% TODO: error handling if the date is badly formatted
|
|
{"endDate", EndDateVal} -> ts_json:decode_datetime(EndDateVal);
|
|
false -> calendar:now_to_universal_time(erlang:now())
|
|
end,
|
|
|
|
% read sort order and list entries
|
|
Entries = case lists:keyfind("order", 1, QueryData) of
|
|
% 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 (Entry) ->
|
|
ts_json:record_to_ejson(Entry,
|
|
ts_ext_data:get_properties(Entry))
|
|
end,
|
|
Entries)},
|
|
|
|
make_json_200(YArg, EJSONEntries);
|
|
|
|
% listing by table position
|
|
_Other ->
|
|
|
|
% read or default the Start
|
|
Start = case lists:keyfind("start", 1, QueryData) of
|
|
{"start", StartVal} -> list_to_integer(StartVal);
|
|
false -> 0
|
|
end,
|
|
|
|
% read or default the Length
|
|
Length = case lists:keyfind("length", 1, QueryData) 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, QueryData) of
|
|
{"order", "desc"} -> ts_entry:list_desc(
|
|
{Username, TimelineId}, Start, Length);
|
|
|
|
_UnknownOrder -> ts_entry:list_asc(
|
|
{Username, TimelineId}, Start, Length)
|
|
end,
|
|
|
|
EJSONEntries = {array, lists:map(
|
|
fun (Entry) ->
|
|
ts_json:record_to_ejson(Entry,
|
|
ts_ext_data:get_properties(Entry))
|
|
end,
|
|
Entries)},
|
|
|
|
make_json_200(YArg, EJSONEntries)
|
|
end.
|
|
|
|
get_entry(YArg, Username, TimelineId, EntryId) ->
|
|
case ts_entry:lookup(Username, TimelineId, EntryId) of
|
|
% no such entry
|
|
no_record -> make_json_404(YArg, [{"error", "no such entry"}]);
|
|
% return the entry data
|
|
Entry -> make_json_200_record(YArg, Entry)
|
|
end.
|
|
|
|
post_entry(YArg, Username, TimelineId) ->
|
|
|
|
% parse the request body
|
|
EJSON = parse_json_body(YArg),
|
|
|
|
% parse into ts_entry record
|
|
{ER, ExtData} = try ts_json:ejson_to_record_strict(
|
|
#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}]))
|
|
end,
|
|
|
|
case ts_entry:new(ER, ExtData) of
|
|
% record created
|
|
{ok, CreatedRecord} ->
|
|
|
|
[{status, 201}, make_json_200_record(YArg, CreatedRecord)];
|
|
|
|
OtherError ->
|
|
error_logger:error_report("Could not create entry: ~p", [OtherError]),
|
|
make_json_500(YArg, OtherError)
|
|
end.
|
|
|
|
put_entry(YArg, Username, TimelineId, EntryId) ->
|
|
|
|
% parse the POST data
|
|
EJSON = parse_json_body(YArg),
|
|
|
|
% parse into ts_entry record
|
|
{ER, ExtData} = try ts_json:ejson_to_record_strict(
|
|
#ts_entry{ref={Username, TimelineId, EntryId}}, EJSON)
|
|
catch _:InputError ->
|
|
error_logger:error_report("Bad input: ~p", [InputError]),
|
|
throw(make_json_400(YArg))
|
|
end,
|
|
|
|
ts_entry:write(ER, ExtData),
|
|
make_json_200_record(YArg, ER).
|
|
|
|
delete_entry(YArg, Username, TimelineId, EntryId) ->
|
|
|
|
% find the record to delete
|
|
case ts_entry:lookup(Username, TimelineId, EntryId) of
|
|
|
|
no_record -> make_json_404(YArg);
|
|
|
|
Record ->
|
|
% try to delete
|
|
case ts_entry:delete(Record) of
|
|
ok -> {status, 200};
|
|
Error ->
|
|
error_logger:error_report("Error occurred deleting entry record: ~p", [Error]),
|
|
make_json_500(YArg, Error)
|
|
end
|
|
end.
|
|
|
|
% ============================== %
|
|
% ======== UTIL METHODS ======== %
|
|
% ============================== %
|
|
|
|
parse_json_body(YArg) ->
|
|
case catch json:decode([], binary_to_list(YArg#arg.clidata)) of
|
|
{done, {ok, EJSON}, _} -> EJSON;
|
|
Error ->
|
|
% TODO: log error internally
|
|
error_logger:error_report("Error parsing JSON request body: ~p", [Error]),
|
|
throw(make_json_400(YArg))
|
|
end.
|
|
|
|
get_origin_header(YArg) ->
|
|
Headers = (YArg#arg.headers)#headers.other,
|
|
case lists:keyfind("Origin", 3, Headers) of
|
|
false -> "*";
|
|
{http_header, 0, "Origin", _, Origin} -> Origin;
|
|
_ -> make_json_500(YArg, "Unrecognized Origin header.")
|
|
end.
|
|
|
|
make_CORS_options(YArg, AllowedMethods) ->
|
|
[{status, 200},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Methods: ", AllowedMethods]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
make_CORS_options(_YArg, AllowedOrigins, AllowedMethods) ->
|
|
[{status, 200},
|
|
{header, ["Access-Control-Allow-Origin: ", AllowedOrigins]},
|
|
{header, ["Access-Control-Allow-Methods: ", AllowedMethods]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
%% Create a JSON 200 response.
|
|
make_json_200(YArg, EJSONResponse) ->
|
|
JSONResponse = json:encode(EJSONResponse),
|
|
[{content, "application/json", JSONResponse},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
|
|
make_json_200_record(YArg, Record) ->
|
|
RecordExtData = ts_ext_data:get_properties(Record),
|
|
EJSON = ts_json:record_to_ejson(Record, RecordExtData),
|
|
JSONResponse = json:encode(EJSON),
|
|
[{content, "application/json", JSONResponse},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
make_json_400(YArg) -> make_json_400(YArg, []).
|
|
make_json_400(YArg, Fields) ->
|
|
F1 = case lists:keyfind(status, 1, Fields) of
|
|
false -> Fields ++ [{"status", "bad request"}];
|
|
_Else -> Fields
|
|
end,
|
|
|
|
[{status, 400}, {content, "application/json", json:encode({struct, F1})},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
make_json_401(YArg) -> make_json_401(YArg, []).
|
|
make_json_401(YArg, Fields) ->
|
|
% add default status if not provided
|
|
F1 = case lists:keyfind(status, 1, Fields) of
|
|
false -> Fields ++ [{"status", "unauthorized"}];
|
|
_Else -> Fields
|
|
end,
|
|
|
|
[{status, 401},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]},
|
|
{content, "application/json", json:encode({struct, F1})}].
|
|
|
|
%% 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, element(2, (YArg#arg.req)#http_request.path)}],
|
|
|
|
[{status, 404}, {content, "application/json", json:encode({struct, F2})},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
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, io_lib:format("~p", [(YArg#arg.req)#http_request.path])}],
|
|
|
|
[{status, 405}, {content, "application/json", json:encode({struct, F1})},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
make_json_500(YArg, Error) ->
|
|
io:format("Error: ~n~p", [Error]),
|
|
EJSON = {struct, [
|
|
{"status", "internal server error"},
|
|
{"error", lists:flatten(io_lib:format("~p", [Error]))}]},
|
|
[{status, 500}, {content, "application/json", json:encode(EJSON)},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|
|
|
|
make_json_500(YArg) ->
|
|
EJSON = {struct, [
|
|
{"status", "internal server error"}]},
|
|
[{status, 500}, {content, "application/json", json:encode(EJSON)},
|
|
{header, ["Access-Control-Allow-Origin: ", get_origin_header(YArg)]},
|
|
{header, ["Access-Control-Allow-Credentials: ", "true"]}].
|