diff --git a/.travis.yml b/.travis.yml index ff7d7b8..5a9b112 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: elixir elixir: - - 1.6 - 1.7 + - 1.8 otp_release: - - 20.3 - 21.0 + - 22.3 notifications: recipients: - travis@jeffweiss.org @@ -18,6 +18,4 @@ cache: script: - mix do deps.get, deps.compile, compile - mix test - - if [[ `elixir -v` = *"1.7"* ]]; then - travis_wait 30 mix dialyzer --halt-exit-status --format term; - fi + - travis_wait 30 mix dialyzer --halt-exit-status --format term; diff --git a/lib/forcex/api/http.ex b/lib/forcex/api/http.ex index bd0afea..cf67588 100644 --- a/lib/forcex/api/http.ex +++ b/lib/forcex/api/http.ex @@ -15,7 +15,7 @@ defmodule Forcex.Api.Http do @type forcex_response :: map | {number, any} | String.t def raw_request(method, url, body, headers, options) do - response = method |> request!(url, body, headers, extra_options() ++ options) |> process_response + response = method |> request!(url, body, headers, extra_options() ++ options) |> process_response() Logger.debug("#{__ENV__.module}.#{elem(__ENV__.function, 0)} response=" <> inspect(response)) response end @@ -25,29 +25,41 @@ defmodule Forcex.Api.Http do Application.get_env(:forcex, :request_options, []) end - @spec process_response(HTTPoison.Response.t) :: forcex_response - defp process_response(%HTTPoison.Response{body: body, headers: %{"Content-Encoding" => "gzip"} = headers} = resp) do - %{resp | body: :zlib.gunzip(body), headers: Map.drop(headers, ["Content-Encoding"])} - |> process_response - end - defp process_response(%HTTPoison.Response{body: body, headers: %{"Content-Encoding" => "deflate"} = headers} = resp) do - zstream = :zlib.open - :ok = :zlib.inflateInit(zstream, -15) - uncompressed_data = zstream |> :zlib.inflate(body) |> Enum.join - :zlib.inflateEnd(zstream) - :zlib.close(zstream) - %{resp | body: uncompressed_data, headers: Map.drop(headers, ["Content-Encoding"])} - |> process_response - end - defp process_response(%HTTPoison.Response{body: body, headers: %{"Content-Type" => "application/json" <> _} = headers} = resp) do - %{resp | body: Poison.decode!(body, keys: :atoms), headers: Map.drop(headers, ["Content-Type"])} - |> process_response + def process_response(%HTTPoison.Response{body: body, headers: headers} = resp) do + cond do + "gzip" = find_header(headers, "Content-Encoding") -> + %{resp | body: :zlib.gunzip(body), headers: List.keydelete(headers, "Content-Encoding", 0)} + |> process_response() + + "deflate" = find_header(headers, "Content-Encoding") -> + zstream = :zlib.open + :ok = :zlib.inflateInit(zstream, -15) + uncompressed_data = zstream |> :zlib.inflate(body) |> Enum.join + :zlib.inflateEnd(zstream) + :zlib.close(zstream) + %{resp | body: uncompressed_data, headers: List.delete(headers, {"Content-Encoding", "deflate"})} + |> process_response() + + "application/json" <> suffix = find_header(headers, "Content-Type") -> + %{resp | body: Poison.decode!(body, keys: :atoms), headers: List.delete(headers, {"Content-Type", "application/json" <> suffix})} + |> process_response() + true -> + resp + end end - defp process_response(%HTTPoison.Response{body: body, status_code: 200}), do: body - defp process_response(%HTTPoison.Response{body: body, status_code: status}), do: {status, body} + def process_response(%HTTPoison.Response{body: body, status_code: 200}), do: body + def process_response(%HTTPoison.Response{body: body, status_code: status}), do: {status, body} def process_request_headers(headers), do: headers ++ @user_agent ++ @accept ++ @accept_encoding - @spec process_headers(list({String.t, String.t})) :: map def process_headers(headers), do: Map.new(headers) + + defp find_header(headers, header_name) do + Enum.find_value( + headers, + fn {name, value} -> + name =~ ~r/#{header_name}/i && String.downcase(value) + end + ) + end end diff --git a/lib/forcex/bulk.ex b/lib/forcex/bulk.ex index 83b8ecf..9b21290 100644 --- a/lib/forcex/bulk.ex +++ b/lib/forcex/bulk.ex @@ -16,14 +16,20 @@ defmodule Forcex.Bulk do def process_headers(headers), do: Map.new(headers) - def process_response(%HTTPoison.Response{body: body, headers: %{"Content-Encoding" => "gzip"} = headers } = resp) do - %{resp | body: :zlib.gunzip(body), headers: Map.drop(headers, ["Content-Encoding"])} - |> process_response - end - def process_response(%HTTPoison.Response{body: body, headers: %{"Content-Type" => "application/json" <> _} = headers} = resp) do - %{resp | body: Poison.decode!(body, keys: :atoms), headers: Map.drop(headers, ["Content-Type"])} - |> process_response + def process_response(%HTTPoison.Response{body: body, headers: headers} = resp) do + cond do + "gzip" = find_header(headers, "Content-Encoding") -> + %{resp | body: :zlib.gunzip(body), headers: List.delete(headers, {"Content-Encoding", "gzip"})} + |> process_response() + + "application/json" <> suffix = find_header(headers, "Content-Type") -> + %{resp | body: Poison.decode!(body, keys: :atoms), headers: List.delete(headers, {"Content-Type", "application/json" <> suffix})} + |> process_response() + true -> + resp + end end + def process_response(%HTTPoison.Response{body: body, status_code: status}) when status < 300 and status >= 200, do: body def process_response(%HTTPoison.Response{body: body, status_code: status}), do: {status, body} @@ -44,12 +50,12 @@ defmodule Forcex.Bulk do request!(method, url, body, headers, extra_options() ++ options) |> process_response end - def get(path, headers \\ [], client) do + def client_get(path, headers \\ [], client) do url = "https://#{client.host}/services/async/#{client.api_version}" <> path raw_request(:get, url, "", headers ++ authorization_header(client), []) end - def post(path, body \\ "", client) do + def client_post(path, body \\ "", client) do url = "https://#{client.host}/services/async/#{client.api_version}" <> path json_request(:post, url, body, authorization_header(client), []) end @@ -57,7 +63,7 @@ defmodule Forcex.Bulk do @spec create_query_job(binary, map) :: job def create_query_job(sobject, client) do payload = %{"operation" => "query", "object" => sobject, "concurrencyMode" => "Parallel", "contentType" => "JSON"} - post("/job", payload, client) + client_post("/job", payload, client) end @spec close_job(job | id, map) :: job @@ -65,13 +71,13 @@ defmodule Forcex.Bulk do close_job(job.id, client) end def close_job(id, client) when is_binary(id) do - post("/job/#{id}", %{"state" => "Closed"}, client) + client_post("/job/#{id}", %{"state" => "Closed"}, client) end @spec fetch_job_status(job | id, map) :: job def fetch_job_status(job, client) when is_map(job), do: fetch_job_status(job.id, client) def fetch_job_status(id, client) when is_binary(id) do - get("/job/#{id}", client) + client_get("/job/#{id}", client) end @spec create_query_batch(String.t, job | id, map) :: job @@ -90,7 +96,7 @@ defmodule Forcex.Bulk do fetch_batch_status(id, job.id, client) end def fetch_batch_status(id, job_id, client) when is_binary(id) and is_binary(job_id) do - get("/job/#{job_id}/batch/#{id}", client) + client_get("/job/#{job_id}/batch/#{id}", client) end @spec fetch_batch_result_status(batch, map) :: list(String.t) @@ -99,7 +105,7 @@ defmodule Forcex.Bulk do end @spec fetch_batch_result_status(id, id, map) :: list(String.t) def fetch_batch_result_status(batch_id, job_id, client) when is_binary(batch_id) and is_binary(job_id) do - get("/job/#{job_id}/batch/#{batch_id}/result", client) + client_get("/job/#{job_id}/batch/#{batch_id}/result", client) end @spec fetch_results(id, batch, map) :: list(map) @@ -108,7 +114,15 @@ defmodule Forcex.Bulk do end @spec fetch_results(id, id, id, map) :: list(map) def fetch_results(id, batch_id, job_id, client) when is_binary(id) and is_binary(batch_id) and is_binary(job_id) do - get("/job/#{job_id}/batch/#{batch_id}/result/#{id}", client) + client_get("/job/#{job_id}/batch/#{batch_id}/result/#{id}", client) end + defp find_header(headers, header_name) do + Enum.find_value( + headers, + fn {name, value} -> + name =~ ~r/#{header_name}/i && String.downcase(value) + end + ) + end end diff --git a/lib/forcex/bulk/batch_worker.ex b/lib/forcex/bulk/batch_worker.ex index 8adfc39..657e3df 100644 --- a/lib/forcex/bulk/batch_worker.ex +++ b/lib/forcex/bulk/batch_worker.ex @@ -39,12 +39,8 @@ defmodule Forcex.Bulk.BatchWorker do seen_results = Keyword.get(state, :results, []) results = Forcex.Bulk.fetch_batch_result_status(batch, client) - case (results -- seen_results) do - list when is_list(list) -> - for result <- list do - notify_handlers({:batch_partial_result_ready, batch, result}, handlers) - end - _ -> true + for result <- (results -- seen_results) do + notify_handlers({:batch_partial_result_ready, batch, result}, handlers) end Keyword.put(state, :results, results) diff --git a/mix.lock b/mix.lock index f128d28..e5f80f0 100644 --- a/mix.lock +++ b/mix.lock @@ -1,31 +1,32 @@ %{ - "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, - "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, - "dialyxir": {:hex, :dialyxir, "1.0.0-rc.3", "774306f84973fc3f1e2e8743eeaa5f5d29b117f3916e5de74c075c02f1b8ef55", [:mix], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, - "erlsom": {:hex, :erlsom, "1.4.2", "5cddb82fb512f406f61162e511ae86582f824f0dccda788378b18a00d89c1b3f", [:rebar3], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.10.0", "a4508bdd408829f38e7b2519f234b7fd5c83846099cda348efcb5291b081200c", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, - "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, - "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"}, - "gettext": {:hex, :gettext, "0.16.0", "4a7e90408cef5f1bf57c5a39e2db8c372a906031cc9b1466e963101cb927dafc", [:mix], [], "hexpm"}, - "hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, - "html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"}, - "httpoison": {:hex, :httpoison, "1.4.0", "e0b3c2ad6fa573134e42194d13e925acfa8f89d138bc621ffb7b1989e6d22e73", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, - "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, - "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, - "makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, - "mix_test_watch": {:hex, :mix_test_watch, "0.9.0", "c72132a6071261893518fa08e121e911c9358713f62794a90c95db59042af375", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"}, - "mox": {:hex, :mox, "0.4.0", "7f120840f7d626184a3d65de36189ca6f37d432e5d63acd80045198e4c5f7e6e", [:mix], [], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"}, - "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, - "timex": {:hex, :timex, "3.3.0", "e0695aa0ddb37d460d93a2db34d332c2c95a40c27edf22fbfea22eb8910a9c8d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, - "tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, + "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "01d479edba0569a7b7a2c8bf923feeb6dc6a358edc2965ef69aea9ba288bb243"}, + "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, + "dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"}, + "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm", "b42a23e9bd92d65d16db2f75553982e58519054095356a418bb8320bbacb58b1"}, + "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, + "erlsom": {:hex, :erlsom, "1.4.2", "5cddb82fb512f406f61162e511ae86582f824f0dccda788378b18a00d89c1b3f", [:rebar3], [], "hexpm", "ac989e850a5a4c1641694f77506804710315f3d1193c977a36b223a32859edd3"}, + "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "dc87f778d8260da0189a622f62790f6202af72f2f3dee6e78d91a18dd2fcd137"}, + "excoveralls": {:hex, :excoveralls, "0.10.0", "a4508bdd408829f38e7b2519f234b7fd5c83846099cda348efcb5291b081200c", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "74d87b5642251722b94a5bcf493a409c01ebba8962ff79b47365172b11c0280d"}, + "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, + "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm", "0d50da6b04c58e101a3793b1600f9a03b86e3a8057b192ac1766013d35706fa6"}, + "gettext": {:hex, :gettext, "0.16.0", "4a7e90408cef5f1bf57c5a39e2db8c372a906031cc9b1466e963101cb927dafc", [:mix], [], "hexpm", "80493964a1ef9c4013486c9b6fdb1c1448d0d30ddf113678334b557724decf42"}, + "hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "ed15491f324aa0e95647dca8ef4340418dac479d1204d57e455d52dcfba3f705"}, + "html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm", "3e3d7156a272950373ce5a4018b1490bea26676f8d6a7d409f6fac8568b8cb9a"}, + "httpoison": {:hex, :httpoison, "1.4.0", "e0b3c2ad6fa573134e42194d13e925acfa8f89d138bc621ffb7b1989e6d22e73", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "37b6f39cb92136ee72276f4bf4da81495e7935d720c3a15daf1553953153e3f7"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, + "jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "639645cfac325e34938167b272bae0791fea3a34cf32c29525abf1d323ed4c18"}, + "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"}, + "makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d7152ff93f2eac07905f510dfa03397134345ba4673a00fbf7119bab98632940"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "4a36dd2d0d5c5f98d95b3f410d7071cd661d5af310472229dd0e92161f168a44"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm", "7a4c8e1115a2732a67d7624e28cf6c9f30c66711a9e92928e745c255887ba465"}, + "mix_test_watch": {:hex, :mix_test_watch, "0.9.0", "c72132a6071261893518fa08e121e911c9358713f62794a90c95db59042af375", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "817dec4a7f6edf260258002f99ac8ffaf7a8f395b27bf2d13ec24018beecec8a"}, + "mox": {:hex, :mox, "0.4.0", "7f120840f7d626184a3d65de36189ca6f37d432e5d63acd80045198e4c5f7e6e", [:mix], [], "hexpm", "64fca8aca4699cc1acf7db19237ce781cfa46683082a440e9f4e1ac29e290d89"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm", "ebb595e19456a72786db6dcd370d320350cb624f0b6203fcc7e23161d49b0ffb"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm", "603561dc0fd62f4f2ea9b890f4e20e1a0d388746d6e20557cafb1b16950de88c"}, + "timex": {:hex, :timex, "3.3.0", "e0695aa0ddb37d460d93a2db34d332c2c95a40c27edf22fbfea22eb8910a9c8d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "87a1644b84d9f9db438e194ce23a59c8f7e0198ec9e8c33eaa7a34d543896be1"}, + "tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9d73d54f2cd2780da2cb8148c5f9cd35ddc5639467568d2297002ca20ea6bbf7"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, }