Skip to content

Commit c543819

Browse files
committed
Add put method
1 parent 1625361 commit c543819

File tree

2 files changed

+45
-23
lines changed

2 files changed

+45
-23
lines changed

deps/rabbitmq_aws/src/rabbitmq_aws.erl

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
-behavior(gen_server).
1010

1111
%% API exports
12-
-export([get/2, get/3,
12+
-export([get/2, get/3, put/4,
1313
post/4,
1414
refresh_credentials/0,
1515
request/5, request/6, request/7,
@@ -75,6 +75,18 @@ post(Service, Path, Body, Headers) ->
7575
request(Service, post, Path, Body, Headers).
7676

7777

78+
-spec put(Service :: string(),
79+
Path :: path(),
80+
Body :: body(),
81+
Headers :: headers()) -> result().
82+
%% @doc Perform a HTTP Post request to the AWS API for the specified service. The
83+
%% response will automatically be decoded if it is either in JSON or XML
84+
%% format.
85+
%% @end
86+
put(Service, Path, Body, Headers) ->
87+
request(Service, put, Path, Body, Headers).
88+
89+
7890
-spec refresh_credentials() -> ok | error.
7991
%% @doc Manually refresh the credentials from the environment, filesystem or EC2 Instance Metadata Service.
8092
%% @end
@@ -287,6 +299,8 @@ endpoint_tld(_Other) ->
287299
%% @end
288300
format_response({ok, {{_Version, 200, _Message}, Headers, Body}}) ->
289301
{ok, {Headers, maybe_decode_body(get_content_type(Headers), Body)}};
302+
format_response({ok, {{_Version, 206, _Message}, Headers, Body}}) ->
303+
{ok, {Headers, maybe_decode_body(get_content_type(Headers), Body)}};
290304
format_response({ok, {{_Version, StatusCode, Message}, Headers, Body}}) when StatusCode >= 400 ->
291305
{error, Message, {Headers, maybe_decode_body(get_content_type(Headers), Body)}};
292306
format_response({error, Reason}) ->
@@ -297,9 +311,9 @@ format_response({error, Reason}) ->
297311
%% {Type, Subtype}.
298312
%% @end
299313
get_content_type(Headers) ->
300-
Value = case proplists:get_value("content-type", Headers, undefined) of
314+
Value = case proplists:get_value(<<"content-type">>, Headers, undefined) of
301315
undefined ->
302-
proplists:get_value("Content-Type", Headers, "text/xml");
316+
proplists:get_value(<<"Content-Type">>, Headers, "text/xml");
303317
Other -> Other
304318
end,
305319
parse_content_type(Value).
@@ -368,6 +382,8 @@ local_time() ->
368382
-spec maybe_decode_body(ContentType :: {nonempty_string(), nonempty_string()}, Body :: body()) -> list() | body().
369383
%% @doc Attempt to decode the response body by its MIME
370384
%% @end
385+
maybe_decode_body(_, <<>>) ->
386+
<<>>;
371387
maybe_decode_body({"application", "x-amz-json-1.0"}, Body) ->
372388
rabbitmq_aws_json:decode(Body);
373389
maybe_decode_body({"application", "json"}, Body) ->
@@ -381,6 +397,8 @@ maybe_decode_body(_ContentType, Body) ->
381397
-spec parse_content_type(ContentType :: string()) -> {Type :: string(), Subtype :: string()}.
382398
%% @doc parse a content type string returning a tuple of type/subtype
383399
%% @end
400+
parse_content_type(ContentType) when is_binary(ContentType)->
401+
parse_content_type(binary_to_list(ContentType));
384402
parse_content_type(ContentType) ->
385403
Parts = string:tokens(ContentType, ";"),
386404
[Type, Subtype] = string:tokens(lists:nth(1, Parts), "/"),
@@ -437,25 +455,23 @@ perform_request_creds_expired(true, State, _, _, _, _, _, _, _) ->
437455
%% expired, perform the request and return the response.
438456
%% @end
439457
perform_request_with_creds(State, Service, Method, Headers, Path, Body, Options, Host) ->
440-
URI = endpoint(State, Host, Service, Path),
441-
SignedHeaders = sign_headers(State, Service, Method, URI, Headers, Body),
442-
ContentType = proplists:get_value("content-type", SignedHeaders, undefined),
443-
perform_request_with_creds(State, Method, URI, SignedHeaders, ContentType, Body, Options).
458+
URI = endpoint(State, Host, Service, Path),
459+
SignedHeaders = sign_headers(State, Service, Method, URI, Headers, Body),
460+
perform_request_with_creds(State, Method, URI, SignedHeaders, Body, Options).
444461

445462

446463
-spec perform_request_with_creds(State :: state(), Method :: method(), URI :: string(),
447-
Headers :: headers(), ContentType :: string() | undefined,
464+
Headers :: headers(),
448465
Body :: body(), Options :: http_options())
449466
-> {Result :: result(), NewState :: state()}.
450467
%% @doc Once it is validated that there are credentials to try and that they have not
451468
%% expired, perform the request and return the response.
452469
%% @end
453-
perform_request_with_creds(State, Method, URI, Headers, undefined, "", Options0) ->
470+
perform_request_with_creds(State, Method, URI, Headers, "", Options0) ->
454471
{Response, NewState} = gun_request(State, Method, URI, Headers, <<>>, Options0),
455472
{format_response(Response), NewState};
456-
perform_request_with_creds(State, Method, URI, Headers, ContentType, Body, Options0) ->
457-
GunHeaders = [{"content-type", ContentType} | Headers],
458-
{Response, NewState} = gun_request(State, Method, URI, GunHeaders, Body, Options0),
473+
perform_request_with_creds(State, Method, URI, Headers, Body, Options0) ->
474+
{Response, NewState} = gun_request(State, Method, URI, Headers, Body, Options0),
459475
{format_response(Response), NewState}.
460476

461477

@@ -580,11 +596,15 @@ api_get_request_with_retries(Service, Path, Retries, WaitTimeBetweenRetries) ->
580596

581597
%% Gun HTTP client functions
582598
gun_request(State, Method, URI, Headers, Body, Options) ->
599+
HeadersBin = lists:map(
600+
fun({Key, Value}) ->
601+
{list_to_binary(Key), list_to_binary(Value)}
602+
end, Headers),
583603
{Host, Port, Path} = parse_uri(URI),
584604
{ConnPid, NewState} = get_or_create_gun_connection(State, Host, Port, Path, Options),
585605
Timeout = proplists:get_value(timeout, Options, ?DEFAULT_HTTP_TIMEOUT),
586606
try
587-
StreamRef = do_gun_request(ConnPid, Method, Path, Headers, Body),
607+
StreamRef = do_gun_request(ConnPid, Method, Path, HeadersBin, Body),
588608
case gun:await(ConnPid, StreamRef, Timeout) of
589609
{response, fin, Status, RespHeaders} ->
590610
Response = {ok, {{http_version, Status, status_text(Status)}, RespHeaders, <<>>}},
@@ -637,7 +657,7 @@ get_or_create_gun_connection(State, Host, Port, Path, Options) ->
637657
end.
638658

639659
get_connection_key(Host, Port, Path, Options) ->
640-
case proplists:get_value(connection_per_path, Options, false) of
660+
case proplists:get_value(connection_per_path, Options, true) of
641661
true -> Host ++ ":" ++ integer_to_list(Port) ++ Path; % Per-path
642662
false -> Host ++ ":" ++ integer_to_list(Port) % Per-host (default)
643663
end.
@@ -700,9 +720,11 @@ parse_host_port(HostPort, Scheme) ->
700720
end.
701721

702722
status_text(200) -> "OK";
723+
status_text(206) -> "Partial Content";
703724
status_text(400) -> "Bad Request";
704725
status_text(401) -> "Unauthorized";
705726
status_text(403) -> "Forbidden";
706727
status_text(404) -> "Not Found";
728+
status_text(416) -> "Range Not Satisfiable";
707729
status_text(500) -> "Internal Server Error";
708730
status_text(Code) -> integer_to_list(Code).

deps/rabbitmq_aws/test/rabbitmq_aws_tests.erl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ expired_credentials_test_() ->
120120
format_response_test_() ->
121121
[
122122
{"ok", fun() ->
123-
Response = {ok, {{"HTTP/1.1", 200, "Ok"}, [{"Content-Type", "text/xml"}], "<test>Value</test>"}},
124-
Expectation = {ok, {[{"Content-Type", "text/xml"}], [{"test", "Value"}]}},
123+
Response = {ok, {{"HTTP/1.1", 200, "Ok"}, [{<<"Content-Type">>, <<"text/xml">>}], "<test>Value</test>"}},
124+
Expectation = {ok, {[{<<"Content-Type">>, <<"text/xml">>}], [{"test", "Value"}]}},
125125
?assertEqual(Expectation, rabbitmq_aws:format_response(Response))
126126
end},
127127
{"error", fun() ->
@@ -172,13 +172,13 @@ gen_server_call_test_() ->
172172
%% {ok, {{"HTTP/1.0", 200, "OK"}, [{"content-type", "application/json"}], "{\"pass\": true}"}}
173173
%% end),
174174
meck:expect(gun, await,
175-
fun(_Pid, _, _) -> {response, nofin, 200, [{"content-type", "application/json"}]} end),
175+
fun(_Pid, _, _) -> {response, nofin, 200, [{<<"content-type">>, <<"application/json">>}]} end),
176176
meck:expect(gun, await_body,
177177
fun(_Pid, _, _) -> {ok, <<"{\"pass\": true}">>} end),
178178

179179
%% {ok, {{"HTTP/1.0", 200, "OK"}, [{"content-type", "application/json"}], "{\"pass\": true}"}}
180180
%% end),
181-
Expectation = {reply, {ok, {[{"content-type", "application/json"}], [{"pass", true}]}}, State#state{gun_connections = #{"ec2.us-east-1.amazonaws.com:443" => pid}}},
181+
Expectation = {reply, {ok, {[{<<"content-type">>, <<"application/json">>}], [{"pass", true}]}}, State#state{gun_connections = #{"ec2.us-east-1.amazonaws.com:443" => pid}}},
182182
Result = rabbitmq_aws:handle_call({request, Service, Method, Headers, Path, Body, Options, Host}, eunit, State),
183183
?assertEqual(Expectation, Result),
184184
meck:validate(gun)
@@ -364,11 +364,11 @@ perform_request_test_() ->
364364
meck:expect(gun, get,
365365
fun(_Pid, "/?Action=DescribeTags&Version=2015-10-01", _Headers) -> nofin end),
366366
meck:expect(gun, await,
367-
fun(_Pid, _, _) -> {response, nofin, 200, [{"content-type", "application/json"}]} end),
367+
fun(_Pid, _, _) -> {response, nofin, 200, [{<<"content-type">>, <<"application/json">>}]} end),
368368
meck:expect(gun, await_body,
369369
fun(_Pid, _, _) -> {ok, <<"{\"pass\": true}">>} end),
370370

371-
Expectation = {{ok, {[{"content-type", "application/json"}], [{"pass", true}]}}, State#state{gun_connections = #{"ec2.us-east-1.amazonaws.com:443" => pid}}},
371+
Expectation = {{ok, {[{<<"content-type">>, <<"application/json">>}], [{"pass", true}]}}, State#state{gun_connections = #{"ec2.us-east-1.amazonaws.com:443" => pid}}},
372372
Result = rabbitmq_aws:perform_request(State, Service, Method, Headers, Path, Body, Options, Host),
373373
?assertEqual(Expectation, Result),
374374
meck:validate(gun)
@@ -476,7 +476,7 @@ api_get_request_test_() ->
476476
meck:expect(gun, get,
477477
fun(_Pid, _Path, _Headers) -> nofin end),
478478
meck:expect(gun, await,
479-
fun(_Pid, _, _) -> {response, nofin, 200, [{"content-type", "application/json"}]} end),
479+
fun(_Pid, _, _) -> {response, nofin, 200, [{<<"content-type">>, <<"application/json">>}]} end),
480480
meck:expect(gun, await_body,
481481
fun(_Pid, _, _) -> {ok, <<"{\"data\": \"value\"}">>} end),
482482

@@ -540,8 +540,8 @@ api_get_request_test_() ->
540540
%% meck:expect(gun, get, 3, meck:seq(
541541
%% fun(_Pid, _Path, _Headers) -> {error, "network errors"} end),
542542
meck:expect(gun, await, 3, meck:seq([{error, "network error"},
543-
{response, nofin, 500, [{"content-type", "application/json"}]},
544-
{response, nofin, 200, [{"content-type", "application/json"}]}])),
543+
{response, nofin, 500, [{<<"content-type">>, <<"application/json">>}]},
544+
{response, nofin, 200, [{<<"content-type">>, <<"application/json">>}]}])),
545545

546546
meck:expect(gun, await_body, 3, meck:seq([{ok, <<"{\"error\": \"server error\"}">>},
547547
{ok, <<"{\"data\": \"value\"}">>}])),

0 commit comments

Comments
 (0)