Reference implementation of Hex specifications: https://github.com/hexpm/specifications.
Let's use default config for now. See "Configuration" section below for customization.
Config = hex_core:default_config().WARNING The built-in httpc-based HTTP client adapter has insecure default settings
and thus it is not recommended for usage, in particular for API write operations.
Use HTTP clients with secure defaults like hackney and Mint.
See "Configuration" and "Wrapper Module" sections for more information on customising your usage of hex_core.
Get all package names:
> hex_repo:get_names(Config).
{ok,{200, ...,
#{packages => [
#{name => <<"package1">>},
#{name => <<"package2">>},
...]}}}Get all package versions from repository:
> hex_repo:get_versions(Config).
{ok,{200, ...,
#{packages => [[
#{name => <<"package1">>, retired => [], versions => [<<"1.0.0">>]},
#{name => <<"package2">>, retired => [], versions => [<<"0.5.0">>]},
...]}}}Get package releases from repository:
> hex_repo:get_package(Config, <<"package1">>).
{ok,{200, ...,
#{releases => [
#{checksum => ..., version => <<"0.5.0">>, dependencies => []}],
#{checksum => ..., version => <<"1.0.0">>, dependencies => []}],
...]}}}For a full list of all parameters and returned objects for the API, check out the API docs: https://github.com/hexpm/specifications/blob/master/http_api.md.
Get package from HTTP API:
> hex_api_package:get(Config, <<"package1">>).
{ok, {200, ...,
#{
<<"name">> => <<"package1">>,
<<"meta">> => #{
<<"description">> => ...,
<<"licenses">> => ...,
<<"links">> => ...,
<<"maintainers">> => ...,
},
...,
<<"releases">> => [
#{<<"url">> => ..., <<"version">> => <<"0.5.0">>}],
#{<<"url">> => ..., <<"version">> => <<"1.0.0">>}],
...
]
}}}Get package tarball:
{ok, {200, _, Tarball}} = hex_repo:get_tarball(Config, <<"package1">>, <<"1.0.0">>).Publish package tarball:
{ok, {200, _Headers, _Body} = hex_api_package:publish(Config, Tarball).When using OAuth tokens, two-factor authentication may be required. If required, the server will return {error, otp_required} and you should retry the request with the TOTP code via the api_otp configuration option. If the TOTP code is incorrect, the server will return {error, invalid_totp} and you should retry with the correct TOTP code.
%% First attempt without OTP
case hex_api_release:publish(Config, Tarball) of
{error, otp_required} ->
%% Retry with TOTP code
ConfigWithOTP = Config#{api_otp := <<"123456">>},
hex_api_release:publish(ConfigWithOTP, Tarball);
{error, invalid_totp} ->
%% Retry with correct TOTP code
ConfigWithOTP = Config#{api_otp := <<"123456">>},
hex_api_release:publish(ConfigWithOTP, Tarball);
Result ->
Result
end.API keys don't require TOTP validation.
Unpack package tarball:
{ok, #{outer_checksum := Checksum, contents := Contents, metadata := Metadata}} = hex_tarball:unpack(Tarball, memory).Remember to verify the outer tarball checksum against the registry checksum
returned from hex_repo:get_package(Config, Package).
Create package tarball:
{ok, #{tarball := Tarball,
inner_checksum := InnerChecksum,
outer_checksum := OuterChecksum}} = hex_tarball:create(Metadata, Contents).The default configuration, provided by hex_core:default_config/0, uses built-in httpc-based adapter and Hex.pm APIs:
https://hex.pm/api and https://repo.hex.pm.
HTTP client configuration can be overridden as follows:
Config = maps:merge(hex_core:default_config(), #{
http_adapter => {my_hackney_adapter, my_hackney_adapter_config},
http_user_agent_fragment => <<"(my_app/0.1.0) (hackney/1.12.1) ">>
}),
hex_repo:get_names(Config).
%% my_hackney_adapter.erl
-module(my_hackney_adapter).
-behaviour(hex_http).
-exports([request/5]).
request(Method, URI, ReqHeaders, Body, AdapterConfig) ->
%% ...See the hex_core module for more information about the configuration.
It's recommended to write a wrapper module because a lot of decisions are left to the user, e.g.: where to get configuration from, how to handle caching, failures etc.
For a sample, see: examples/myapp_hex.erl. Here's an excerpt:
-module(myapp_hex).
-export([
get_api_package/1,
get_repo_tarball/2,
get_repo_versions/0
]).
%%====================================================================
%% API functions
%%====================================================================
get_api_package(Name) ->
case hex_api_package:get(config(), Name) of
{ok, {200, _Headers, Payload}} ->
{ok, Payload};
Other ->
Other
end.
get_repo_versions() ->
case hex_repo:get_versions(config()) of
{ok, {200, _Headers, Payload}} ->
{ok, maps:get(packages, Payload)};
Other ->
Other
end.
%%====================================================================
%% Internal functions
%%====================================================================
config() ->
Config1 = hex_core:default_config(),
Config2 = put_http_config(Config1),
Config3 = maybe_put_api_key(Config2),
Config3.
put_http_config(Config) ->
maps:put(http_user_agent_fragment, <<"(myapp/1.0.0) (httpc)">>, Config).
maybe_put_api_key(Config) ->
case os:getenv("HEX_API_KEY") of
false -> Config;
Key -> maps:put(api_key, Key, Config)
end.Add to rebar.config:
{deps, [
{hex_core, "0.12.0"}
]}Add to mix.exs:
defp deps do
[
{:hex_core, "~> 0.12.0"}
]
end- Run
rebar3 as dev compileto re-generate protobuf files - Run
rebar3 as test properfor property-based tests - Run
rebar3 ex_docto generate documentation