/src/hex_api.erl
https://github.com/hexpm/hex_core · Erlang · 163 lines · 98 code · 27 blank · 38 comment · 0 complexity · a539a368729b43fb3bf1716d4486158d MD5 · raw file
- %% @doc
- %% Hex HTTP API
- -module(hex_api).
- -export([
- delete/2,
- get/2,
- post/3,
- put/3,
- encode_query_string/1,
- build_repository_path/2,
- build_organization_path/2,
- join_path_segments/1
- ]).
- -define(ERL_CONTENT_TYPE, <<"application/vnd.hex+erlang">>).
- -export_type([response/0]).
- -type response() :: {ok, {hex_http:status(), hex_http:headers(), body() | nil}} | {error, term()}.
- -type body() :: [body()] | #{binary() => body() | binary()}.
- %% @private
- get(Config, Path) ->
- request(Config, get, Path, undefined).
- %% @private
- post(Config, Path, Body) ->
- request(Config, post, Path, encode_body(Body)).
- %% @private
- put(Config, Path, Body) ->
- request(Config, put, Path, encode_body(Body)).
- %% @private
- delete(Config, Path) ->
- request(Config, delete, Path, undefined).
- %% @private
- encode_query_string(List) ->
- Pairs = lists:map(fun ({K, V}) -> {to_list(K), to_list(V)} end, List),
- list_to_binary(compose_query(Pairs)).
- %% OTP 21+
- %% @private
- -ifdef (OTP_RELEASE).
- compose_query(Pairs) ->
- uri_string:compose_query(Pairs).
- -else.
- compose_query(Pairs) ->
- String = join("&", lists:map(fun ({K, V}) -> K ++ "=" ++ V end, Pairs)),
- http_uri:encode(String).
- -endif.
- %% @private
- build_repository_path(#{api_repository := Repo}, Path) when is_binary(Repo) ->
- ["repos", Repo | Path];
- build_repository_path(#{api_repository := undefined}, Path) ->
- Path.
- %% @private
- build_organization_path(#{api_organization := Org}, Path) when is_binary(Org) ->
- ["orgs", Org | Path];
- build_organization_path(#{api_organization := undefined}, Path) ->
- Path.
- %% @private
- join_path_segments(Segments) ->
- iolist_to_binary(recompose(Segments)).
- %% OTP 21+
- %% @private
- -ifdef (OTP_RELEASE).
- recompose(Segments) ->
- Concatenated = join(<<"/">>, Segments),
- %% uri_string:recompose/1 accepts path segments as a list,
- %% both strings and binaries
- uri_string:recompose(#{path => Concatenated}).
- -else.
- recompose(Segments) ->
- join(<<"/">>, lists:map(fun encode_segment/1, Segments)).
- %% @private
- encode_segment(Binary) when is_binary(Binary) ->
- encode_segment(binary_to_list(Binary));
- encode_segment(String) when is_list(String) ->
- http_uri:encode(String).
- -endif.
- %%====================================================================
- %% Internal functions
- %%====================================================================
- request(Config, Method, PathSegments, Body) when is_list(PathSegments) ->
- Path = join_path_segments(PathSegments),
- request(Config, Method, Path, Body);
- request(Config, Method, Path, Body) when is_binary(Path) and is_map(Config) ->
- DefaultHeaders = make_headers(Config),
- ReqHeaders = maps:merge(maps:get(http_headers, Config, #{}), DefaultHeaders),
- ReqHeaders2 = put_new(<<"accept">>, ?ERL_CONTENT_TYPE, ReqHeaders),
- case hex_http:request(Config, Method, build_url(Path, Config), ReqHeaders2, Body) of
- {ok, {Status, RespHeaders, RespBody}} ->
- ContentType = maps:get(<<"content-type">>, RespHeaders, <<"">>),
- case binary:match(ContentType, ?ERL_CONTENT_TYPE) of
- {_, _} ->
- {ok, {Status, RespHeaders, binary_to_term(RespBody)}};
- nomatch ->
- {ok, {Status, RespHeaders, nil}}
- end;
- Other ->
- Other
- end.
- %% TODO: not needed after exdoc is fixed
- %% @private
- build_url(Path, #{api_url := URI}) ->
- <<URI/binary, "/", Path/binary>>.
- %% TODO: not needed after exdoc is fixed
- %% @private
- encode_body({_ContentType, _Body} = Body) ->
- Body;
- encode_body(Body) ->
- {binary_to_list(?ERL_CONTENT_TYPE), term_to_binary(Body)}.
- %% TODO: not needed after exdoc is fixed
- %% @private
- %% TODO: copy-pasted from hex_repo
- make_headers(Config) ->
- maps:fold(fun set_header/3, #{}, Config).
- %% TODO: not needed after exdoc is fixed
- %% @private
- set_header(api_key, Token, Headers) when is_binary(Token) -> maps:put(<<"authorization">>, Token, Headers);
- set_header(_, _, Headers) -> Headers.
- %% TODO: not needed after exdoc is fixed
- %% @private
- put_new(Key, Value, Map) ->
- case maps:find(Key, Map) of
- {ok, _} -> Map;
- error -> maps:put(Key, Value, Map)
- end.
- %% TODO: not needed after exdoc is fixed
- %% @private
- %% https://github.com/erlang/otp/blob/OTP-20.3/lib/stdlib/src/lists.erl#L1449:L1453
- join(_Sep, []) -> [];
- join(Sep, [H|T]) -> [H|join_prepend(Sep, T)].
- %% TODO: not needed after exdoc is fixed
- %% @private
- join_prepend(_Sep, []) -> [];
- join_prepend(Sep, [H|T]) -> [Sep,H|join_prepend(Sep,T)].
- %% TODO: not needed after exdoc is fixed
- %% @private
- to_list(A) when is_atom(A) -> atom_to_list(A);
- to_list(B) when is_binary(B) -> unicode:characters_to_list(B);
- to_list(I) when is_integer(I) -> integer_to_list(I);
- to_list(Str) -> unicode:characters_to_list(Str).