123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- -module(rest_kvs).
- -include_lib("kvs/include/kvs.hrl").
- -compile(export_all).
- -export([exists/1, exists/2, new/1, get/1, delete/2, post/2, post/3]).
- -export([init/2, resource_exists/2, allowed_methods/2, content_types_provided/2,
- to_html/2, to_json/2, content_types_accepted/2, delete_resource/2,
- handle_urlencoded_data/2, handle_json_data/2]).
- -ifndef(REST_JSON).
- -define(REST_JSON, (application:get_env(rest,json,jsone))).
- -endif.
- c(X) when is_tuple(X) -> Module = hd(tuple_to_list(X)), Module:uri(X);
- c(X) -> binary_to_list(X).
- % kvs rest api
- new(Type) -> Type:new().
- exists(Mod) -> {X,_} = kvs:get(writer,c(Mod)), X == ok.
- exists(Mod,Id) -> {X,_} = kvs:get(c(Mod),c(Id)), X == ok.
- get(Mod) -> kvs:all(Mod).
- get(Mod,Id) -> {X,Y} = kvs:get(c(Mod),c(Id)), X == ok, Y.
- delete(Mod,Id) -> kvs:delete(Mod,Id).
- post(Mod,Resource) when is_tuple(Resource) -> kvs:append(Mod,Resource).
- post(Type,Mod,Data) when is_list(Data) -> post(Mod,Type:from_json(new(Type))).
- % cowboy rest api
- update_req(Req) -> #{ bindings := Bindings, path_info := List } = Req,
- Req#{bindings => Bindings#{ resource => list_to_binary("/" ++
- string:join(lists:map(fun (X) -> binary_to_list(X) end, List),"/")) }}.
- init(#{bindings := #{id := Id}} = Req, State) -> {cowboy_rest, update_req(Req), State};
- init(Req, State) -> {cowboy_rest, update_req(Req), State}.
- parse_id(Id) ->
- List = binary_to_list(Id),
- Parsed = case string:tokens(List,",") of
- [X] -> Id;
- _ -> rest:parse(lists:concat(["{",List,"}"]))
- end.
- resource_exists(#{bindings := #{resource := Module, id := Id}} = Req, State) ->
- {rest_kvs:exists(Module,parse_id(Id)), Req, State};
- resource_exists(#{bindings := #{resource := Module}} = Req, State) -> {rest_kvs:exists(Module), Req, State};
- resource_exists(#{bindings := #{id := _}} = Req, State) -> {true, Req, State}.
- allowed_methods(#{bindings := #{resource := _}} = Req, State) -> {[<<"GET">>, <<"POST">>], Req, State};
- allowed_methods(#{bindings := #{resource := _, id := _}} = Req, State) -> {[<<"GET">>, <<"PUT">>, <<"DELETE">>], Req, State}.
- delete_resource(#{bindings := #{resource := Module, id := []}} = Req, State) -> {[], Req, State};
- delete_resource(#{bindings := #{resource := Module, id := Id}} = Req, State) -> {rest_kvs:delete(Module,Id), Req, State}.
- content_types_provided(#{bindings := #{resource := Module}} = Req, State) ->
- {case application:get_env(rest,html,false) of
- false -> [{<<"application/json">>, to_json}];
- true -> [{<<"text/html">>, to_html},
- {<<"application/json">>, to_json}] end, Req, State}.
- % TODO: HTML render broken!
- to_html(#{bindings := #{resource := Module, id := Id}} = Req, State) ->
- % Body = case Id of
- % Id when Id==[];Id==undefined -> [ rest_kvs:to_html(Module, Resource) || Resource <- rest_kvs:get(Module,Id) ];
- % _ -> rest_kvs:to_html(rest_kvs:get(Module,Id)) end,
- % Html = case application:get_env(rest,html_layout,false) of
- % true -> rest_kvs:html_layout(Module, Req, Body);
- % false -> default_html_layout(Body) end,
- {<<>>, Req, State}.
- default_html_layout(Body) -> [<<"<html><body>">>, Body, <<"</body></html>">>].
- % JSON seems fine
- to_json(#{bindings := #{resource := Module, id := Id}} = Req, State) ->
- {ok,Resource} = kvs:get(c(Module),c(parse_id(Id))),
- Type = element(1,Resource),
- {iolist_to_binary([?REST_JSON:encode(rest:binarize(Type:to_json(Resource))),"\n"]), Req, State};
- to_json(#{bindings := #{resource := Module}} = Req, State) ->
- Fold = [ begin M = element(1,Resource), rest:binarize(M:to_json(Resource)) end || Resource <- kvs:all(c(Module))],
- {iolist_to_binary([?REST_JSON:encode([{Module,Fold}]),"\n"]), Req, State}.
- content_types_accepted(Req, State) ->
- {[{<<"application/x-www-form-urlencoded">>, handle_urlencoded_data},
- {<<"application/json">>, handle_json_data}], Req, State}.
- handle_urlencoded_data(#{bindings := #{resource := Module, id := Id}} = Req, State) ->
- {ok, Data, Req2} = cowboy_req:read_urlencoded_body(Req),
- io:format("FORM: ~p~n",[Data]),
- {handle_data(Module, Id, Data, Req), Req2, State};
- handle_urlencoded_data(#{bindings := #{resource := Module}} = Req0, State) ->
- {ok, Data1, Req} = cowboy_req:read_urlencoded_body(Req0),
- io:format("FORM: ~p, Data1: ~p~n",[Module,Data1]),
- {handle_data(Module, [], Data1, Req), Req, State}.
- handle_json_data(#{bindings := #{resource := Module, id := Id}} = Req, State) ->
- {ok, Binary, Req2} = cowboy_req:read_body(Req),
- io:format("JSON: ~p~n",[Binary]),
- Data = case ?REST_JSON:decode(Binary) of {struct, Struct} -> Struct; S -> S end,
- {handle_data(Module, Id, Data, Req), Req2, State};
- handle_json_data(#{bindings := #{resource := Module}} = Req, State) ->
- {ok, Binary, Req2} = cowboy_req:read_body(Req),
- io:format("JSON: ~p~n",[Binary]),
- Data = case ?REST_JSON:decode(Binary) of {struct, Struct} -> Struct; S -> S end,
- {handle_data(Module, [], Data, Req), Req2, State}.
- handle_data(Mod, Id, Data, Req) ->
- Type = proplists:get_value(<<"rec">>, Data),
- Valid = case application:get_env(rest,validate,false) of
- true -> rest_kvs:validate(Mod,Id, Data);
- false -> default_validate(Mod, Id, Data, Req) end,
- case {Valid, Id} of
- {false, _} -> false;
- {true, <<"undefined">>} -> rest_kvs:post(Type,Mod,Data);
- {true, _} -> case application:get_env(rest,custom_put,false) of
- true -> Type:put(Mod, Id, Data);
- false -> default_put(Type, Mod, Id, Data, Req) end
- end.
- validate(_,_,_) -> true.
- keys_allowed(_,_) -> true.
- default_put(Type, Mod, Id, Data, Req) when is_map(Data) -> default_put(Type, Mod, Id, maps:to_list(Data), Req);
- default_put(Type, Mod, Id, Data, Req) ->
- NewRes = Type:from_json(Data, rest_kvs:get(Mod,Id)),
- NewId = proplists:get_value(id, Type:to_json(NewRes)),
- case Id =/= NewId of
- true -> rest_kvs:delete(Mod,Id);
- false -> true end,
- rest_kvs:post(Type,Mod,NewRes).
- default_validate(Mod, Id, DataX, Req0) when is_map(DataX) -> default_validate(Mod, Id, maps:to_list(DataX), Req0);
- default_validate(Mod, Id, Data, Req0) ->
- Allowed = case application:get_env(rest, keys_allowed, false) of
- true -> rest_kvs:keys_allowed(c(Mod),proplists:get_keys(Data));
- false -> true end,
- validate_match(Mod, Id, Allowed, proplists:get_value(<<"id">>, Data)).
- validate_match(_Mod, [], true, []) -> false;
- validate_match( Mod, [], true, NewId) -> not rest_kvs:exists(Mod,NewId);
- validate_match(_Mod, _Id, true, []) -> true;
- validate_match(_Mod, Id, true, Id) -> true;
- validate_match( Mod, _Id, true, NewId) -> not rest_kvs:exists(Mod,NewId);
- validate_match( _, _, _, _) -> false.
|