%%% --------------------------------------------------------------------------------- %%% @doc %%% An example serializer plugin that encodes and deodes broen requests and responses %%% to and form the JSON format. %%% @end %%% --------------------------------------------------------------------------------- -module(broen_serializer_json). -include_lib("eunit/include/eunit.hrl"). %% API -export([serialize/1, deserialize/2]). %% @doc Serialize the broen request into JSON -spec serialize(broen_core:broen_request()) -> {term(), broen_core:content_type()}. serialize(Request) -> MappedReq = maps:map(fun(multipartobj, Val) -> handle_multipart(Val); (_, Val) -> Val end, Request), {jsx:encode(MappedReq), <<"application/json">>}. %% @doc Deserialize the JSON response into a broen response -spec deserialize(term(), broen_core:content_type()) -> {ok, broen_core:broen_response()} | {error, invalid_content_type}. deserialize(Raw, <<"application/json">>) -> Decoded = jsx:decode(Raw, [return_maps]), MappedResponse = maps:from_list(lists:map(fun({K, V}) -> {binary_to_atom(K, utf8), V} end, maps:to_list(Decoded))), MappedCookies = map_cookies(maps:find(cookies, MappedResponse)), {ok, maps:put(cookies, MappedCookies, MappedResponse)}; deserialize(_Raw, _) -> {error, invalid_content_type}. %% Internal functions %% --------------------------------------------------------------------------------- map_cookies(error) -> #{}; map_cookies({ok, Cookies}) -> maps:map(fun(_CookieName, Values) -> maps:from_list(lists:map(fun({K, V}) -> {binary_to_existing_atom(K, utf8), V} end, maps:to_list(Values))) end, Cookies). handle_multipart({MultipartVal}) when is_list(MultipartVal) -> maps:map(fun(<<"body">>, V) -> base64:encode(V); (_K, V) -> handle_multipart(V) end, maps:from_list(MultipartVal)); handle_multipart(Else) -> Else. %% Tests %% --------------------------------------------------------------------------------- serializer_test_() -> Request = #{appmoddata => <<"broen_testing_multipart">>, auth_data => [], client_data => null, client_ip => <<"127.0.0.1">>, cookies => #{}, protocol => http, fullpath => <<"some_fullpath">>, http_headers => #{<<"accept">> => <<"*/*">>, <<"content_length">> => <<"1739">>, <<"content_type">> => <<"multipart/form-data; boundary=------------------------f991051bb9b3059a">>, <<"cookie">> => <<>>, <<"expect">> => <<"100-continue">>, <<"host">> => <<"localhost:7083">>, <<"user_agent">> => <<"curl/7.54.0">>}, method => <<"POST">>, queryobj => #{}, referer => null, request => <<"POST">>, multipartobj => {[{<<"image">>, {[{<<"opts">>, {[{<<"filename">>, <<"header.png">>}, {<<"content_type">>, <<"application/octet-stream">>}]}}, {<<"body">>, <<137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 175, 0, 236, 152, 203, 110, 195, 48, 12, 4, 41, 217, 150, 109, 229, 255>>}]}}]}, routing_key => <<"broen_testing_multipart">>, useragent => <<"curl/7.54.0">>}, {Serialized, _} = serialize(Request), {ok, Deserialized} = deserialize(Serialized, <<"application/json">>), NoMultipartReq = maps:map(fun remove_multipart/2, Request), MappedReq = maps:map(fun transform_protocol/2, NoMultipartReq), NoMultipartDeserialized = maps:map(fun remove_multipart/2, Deserialized), fun() -> ?assert(is_binary(Serialized)), ?assertMatch(MappedReq, NoMultipartDeserialized) end. remove_multipart(multipartobj, _) -> #{}; remove_multipart(_, Val) -> Val. transform_protocol(protocol, Val) -> atom_to_binary(Val, unicode); transform_protocol(_, Val) -> Val.