diff --git a/lib/hex/api.ex b/lib/hex/api.ex index 28675840f..7cb1ff358 100644 --- a/lib/hex/api.ex +++ b/lib/hex/api.ex @@ -1,4 +1,6 @@ defmodule Hex.API do + alias Hex.API.Util + def request(method, url, headers, body \\ nil) when body == nil or is_map(body) do default_headers = %{ 'accept' => 'application/vnd.hex.beta+elixir', @@ -62,7 +64,7 @@ defmodule Hex.API do headers = Enum.into(headers, %{}) content_encoding = :binary.list_to_bin(headers['content-encoding'] || '') content_type = :binary.list_to_bin(headers['content-type'] || '') - handle_hex_message(headers['x-hex-message']) + Util.handle_hex_message(headers['x-hex-message']) if String.contains?(content_encoding, "gzip") do body = :zlib.gunzip(body) @@ -75,18 +77,6 @@ defmodule Hex.API do {code, body} end - @doc false - def handle_hex_message(nil), do: :ok - - def handle_hex_message(header) do - {message, level} = :binary.list_to_bin(header) |> parse_hex_message - case level do - "warn" -> Mix.shell.info("API warning: " <> message) - "fatal" -> Mix.shell.error("API error: " <> message) - _ -> :ok - end - end - def user_agent do 'Hex/#{Hex.version} (Elixir/#{System.version})' end @@ -111,45 +101,4 @@ defmodule Hex.API do base64 = :base64.encode_to_string(info[:user] <> ":" <> info[:pass]) %{'authorization' => 'Basic ' ++ base64} end - - @space [?\s, ?\t] - - defp parse_hex_message(message) do - {message, rest} = skip_ws(message) |> quoted - level = skip_ws(rest) |> opt_level - {message, level} - end - - defp skip_ws(<< char, rest :: binary >>) when char in @space, - do: skip_ws(rest) - defp skip_ws(rest), - do: rest - - defp skip_trail_ws(input, str \\ "", ws \\ "") - - defp skip_trail_ws(<< char, rest :: binary >>, str, ws) when char in @space, - do: skip_trail_ws(rest, str, << ws :: binary, char >>) - defp skip_trail_ws(<< char, rest :: binary >>, str, ws), - do: skip_trail_ws(rest, << str :: binary, ws :: binary, char >>, "") - defp skip_trail_ws("", str, _ws), - do: str - - defp quoted("\"" <> rest), - do: do_quoted(rest, "") - - defp do_quoted("\"" <> rest, acc), - do: {acc, rest} - defp do_quoted(<< char, rest :: binary >>, acc), - do: do_quoted(rest, << acc :: binary, char >>) - - defp opt_level(";" <> rest), - do: do_level(rest) - defp opt_level(_), - do: nil - - defp do_level(rest) do - "level" <> rest = skip_ws(rest) - "=" <> rest = skip_ws(rest) - skip_ws(rest) |> skip_trail_ws - end end diff --git a/lib/hex/api/package.ex b/lib/hex/api/package.ex index 31488375e..e6226d9aa 100644 --- a/lib/hex/api/package.ex +++ b/lib/hex/api/package.ex @@ -8,18 +8,4 @@ defmodule Hex.API.Package do def new(name, meta, auth) do API.request(:put, API.api_url("packages/#{name}"), API.auth(auth), %{meta: meta}) end - - def add_owner(package, owner, auth) do - owner = URI.encode_www_form(owner) - API.request(:put, API.api_url("packages/#{package}/owners/#{owner}"), API.auth(auth)) - end - - def delete_owner(package, owner, auth) do - owner = URI.encode_www_form(owner) - API.request(:delete, API.api_url("packages/#{package}/owners/#{owner}"), API.auth(auth)) - end - - def get_owners(package, auth) do - API.request(:get, API.api_url("packages/#{package}/owners"), API.auth(auth)) - end end diff --git a/lib/hex/api/package_owner.ex b/lib/hex/api/package_owner.ex new file mode 100644 index 000000000..47f628d4d --- /dev/null +++ b/lib/hex/api/package_owner.ex @@ -0,0 +1,17 @@ +defmodule Hex.API.Package.Owner do + alias Hex.API + + def add(package, owner, auth) do + owner = URI.encode_www_form(owner) + API.request(:put, API.api_url("packages/#{package}/owners/#{owner}"), API.auth(auth)) + end + + def delete(package, owner, auth) do + owner = URI.encode_www_form(owner) + API.request(:delete, API.api_url("packages/#{package}/owners/#{owner}"), API.auth(auth)) + end + + def get(package, auth) do + API.request(:get, API.api_url("packages/#{package}/owners"), API.auth(auth)) + end +end diff --git a/lib/hex/api/util.ex b/lib/hex/api/util.ex new file mode 100644 index 000000000..01e092d62 --- /dev/null +++ b/lib/hex/api/util.ex @@ -0,0 +1,54 @@ +defmodule Hex.API.Util do + @doc false + def handle_hex_message(nil), do: :ok + + def handle_hex_message(header) do + {message, level} = :binary.list_to_bin(header) |> parse_hex_message + case level do + "warn" -> Mix.shell.info("API warning: " <> message) + "fatal" -> Mix.shell.error("API error: " <> message) + _ -> :ok + end + end + + @space [?\s, ?\t] + + def parse_hex_message(message) do + {message, rest} = skip_ws(message) |> quoted + level = skip_ws(rest) |> opt_level + {message, level} + end + + def skip_ws(<< char, rest :: binary >>) when char in @space, + do: skip_ws(rest) + def skip_ws(rest), + do: rest + + def skip_trail_ws(input, str \\ "", ws \\ "") + + def skip_trail_ws(<< char, rest :: binary >>, str, ws) when char in @space, + do: skip_trail_ws(rest, str, << ws :: binary, char >>) + def skip_trail_ws(<< char, rest :: binary >>, str, ws), + do: skip_trail_ws(rest, << str :: binary, ws :: binary, char >>, "") + def skip_trail_ws("", str, _ws), + do: str + + def quoted("\"" <> rest), + do: do_quoted(rest, "") + + def do_quoted("\"" <> rest, acc), + do: {acc, rest} + def do_quoted(<< char, rest :: binary >>, acc), + do: do_quoted(rest, << acc :: binary, char >>) + + def opt_level(";" <> rest), + do: do_level(rest) + def opt_level(_), + do: nil + + def do_level(rest) do + "level" <> rest = skip_ws(rest) + "=" <> rest = skip_ws(rest) + skip_ws(rest) |> skip_trail_ws + end +end diff --git a/lib/mix/tasks/hex/owner.ex b/lib/mix/tasks/hex/owner.ex index 09a446464..527feb92b 100644 --- a/lib/mix/tasks/hex/owner.ex +++ b/lib/mix/tasks/hex/owner.ex @@ -52,7 +52,7 @@ defmodule Mix.Tasks.Hex.Owner do defp add_owner(package, owner, opts) do Mix.shell.info("Adding owner #{owner} to #{package}") - case Hex.API.Package.add_owner(package, owner, opts) do + case Hex.API.Package.Owner.add(package, owner, opts) do {204, _body} -> :ok {code, body} -> @@ -63,7 +63,7 @@ defmodule Mix.Tasks.Hex.Owner do defp remove_owner(package, owner, opts) do Mix.shell.info("Removing owner #{owner} from #{package}") - case Hex.API.Package.delete_owner(package, owner, opts) do + case Hex.API.Package.Owner.delete(package, owner, opts) do {204, _body} -> :ok {code, body} -> @@ -73,7 +73,7 @@ defmodule Mix.Tasks.Hex.Owner do end defp list_owners(package, opts) do - case Hex.API.Package.get_owners(package, opts) do + case Hex.API.Package.Owner.get(package, opts) do {200, body} -> Enum.each(body, &Mix.shell.info(&1["email"])) {code, body} -> diff --git a/test/hex/api_test.exs b/test/hex/api_test.exs index f9fe3303b..b2a523576 100644 --- a/test/hex/api_test.exs +++ b/test/hex/api_test.exs @@ -70,26 +70,26 @@ defmodule Hex.APITest do Hex.API.Package.new("orange", %{}, auth) Hex.API.User.new("orange_user", "orange_user@mail.com", "hunter42") - assert {200, [%{"username" => "user"}]} = Hex.API.Package.get_owners("orange", auth) + assert {200, [%{"username" => "user"}]} = Hex.API.Package.Owner.get("orange", auth) - assert {204, _} = Hex.API.Package.add_owner("orange", "orange_user@mail.com", auth) + assert {204, _} = Hex.API.Package.Owner.add("orange", "orange_user@mail.com", auth) assert {200, [%{"username" => "user"}, %{"username" => "orange_user"}]} = - Hex.API.Package.get_owners("orange", auth) + Hex.API.Package.Owner.get("orange", auth) - assert {204, _} = Hex.API.Package.delete_owner("orange", "orange_user@mail.com", auth) + assert {204, _} = Hex.API.Package.Owner.delete("orange", "orange_user@mail.com", auth) - assert {200, [%{"username" => "user"}]} = Hex.API.Package.get_owners("orange", auth) + assert {200, [%{"username" => "user"}]} = Hex.API.Package.Owner.get("orange", auth) end test "x-hex-message" do - Hex.API.handle_hex_message('"oops, you done goofed"') + Hex.API.Util.handle_hex_message('"oops, you done goofed"') refute_received {:mix_shell, _, _} - Hex.API.handle_hex_message(' "oops, you done goofed" ; level = warn') + Hex.API.Util.handle_hex_message(' "oops, you done goofed" ; level = warn') assert_received {:mix_shell, :info, ["API warning: oops, you done goofed"]} - Hex.API.handle_hex_message('"oops, you done goofed";level=fatal ') + Hex.API.Util.handle_hex_message('"oops, you done goofed";level=fatal ') assert_received {:mix_shell, :error, ["API error: oops, you done goofed"]} end end