From bf85cf1cc1bbf9906777eedb930c895e93112c98 Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Tue, 27 May 2025 11:26:23 -0400 Subject: [PATCH 1/4] test: package owner model --- test/aura/model/hex_package_owner_test.exs | 16 ++++++++++++++++ .../factories/hex_package_owner_factory.ex | 15 +++++++++++---- test/support/factory.ex | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 test/aura/model/hex_package_owner_test.exs diff --git a/test/aura/model/hex_package_owner_test.exs b/test/aura/model/hex_package_owner_test.exs new file mode 100644 index 0000000..571a995 --- /dev/null +++ b/test/aura/model/hex_package_owner_test.exs @@ -0,0 +1,16 @@ +defmodule Aura.Model.HexPackageOwnerTest do + use ExUnit.Case + + import Aura.Factory + + alias Aura.Model.HexPackageOwner + + @moduletag :capture_log + + doctest HexPackageOwner + + test "build many" do + list = build_list(100, :hex_package_owner) + assert Enum.count(list) == 100 + end +end diff --git a/test/support/factories/hex_package_owner_factory.ex b/test/support/factories/hex_package_owner_factory.ex index 7ca88f4..3050e62 100644 --- a/test/support/factories/hex_package_owner_factory.ex +++ b/test/support/factories/hex_package_owner_factory.ex @@ -4,11 +4,17 @@ defmodule Aura.Factory.HexPackageOwnerFactory do defmacro __using__(_opts) do quote do def hex_package_owner_factory(attrs) do - # :handles, - # ] - + username = Faker.Internet.user_name() inserted_at = Faker.DateTime.backward(40) + handles = %{ + elixir_form: "https://elixirforum.com/u/#{username}", + git_hub: "https://github.com/#{username}", + twitter: "https://twitter.com/#{username}", + slack: "https://elixir-slackin.herokuapp.com/", + libera: "irc://irc.libera.chat/elixir" + } + %Aura.Model.HexPackageOwner{ email: Faker.Internet.email(), full_name: Faker.Person.name(), @@ -16,7 +22,8 @@ defmodule Aura.Factory.HexPackageOwnerFactory do level: Enum.random([:maintainer, :full]), updated_at: Faker.DateTime.between(inserted_at, DateTime.now!("Etc/UTC")), url: Faker.Internet.url(), - username: Faker.Internet.user_name() + username: username, + handles: handles } |> merge_attributes(attrs) |> evaluate_lazy_attributes() diff --git a/test/support/factory.ex b/test/support/factory.ex index b69d14a..df44135 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -3,5 +3,6 @@ defmodule Aura.Factory do use ExMachina use Aura.Factory.HexRepoFactory use Aura.Factory.HexPackageFactory + use Aura.Factory.HexPackageOwnerFactory use Aura.Factory.HexReleaseFactory end From 3c506c3ac5d84d3e01b5596c950fdf8c8e8565eb Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Tue, 27 May 2025 13:37:53 -0400 Subject: [PATCH 2/4] feat: list api keys --- lib/aura/model/hex_api_key.ex | 31 +++++++++++++++++++++++++++++++ lib/aura/repos.ex | 15 +++++++++++---- lib/aura/util/requester.ex | 17 +++++++++++++++-- mix.exs | 2 +- mix.lock | 1 + test/aura/repos_test.exs | 33 ++++++++++++++++++++++++++++++++- test/test_helper.exs | 5 ++++- 7 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 lib/aura/model/hex_api_key.ex diff --git a/lib/aura/model/hex_api_key.ex b/lib/aura/model/hex_api_key.ex new file mode 100644 index 0000000..5bf8110 --- /dev/null +++ b/lib/aura/model/hex_api_key.ex @@ -0,0 +1,31 @@ +defmodule Aura.Model.HexAPIKey do + @moduledoc false + + import Aura.Model.Common + + defstruct [ + :authing_key, + :inserted_at, + :name, + :permissions, + :updated_at, + :url + ] + + def build(m) when is_map(m) do + m + |> prepare() + |> Enum.map(fn {k, v} -> {k, serialize(k, v)} end) + |> then(&struct(Aura.Model.HexAPIKey, &1)) + end + + defp serialize(_k, nil), do: nil + + defp serialize(:permissions, v) do + v + |> Enum.map(&prepare/1) + |> Enum.map(&Map.new/1) + end + + defp serialize(_k, v), do: v +end diff --git a/lib/aura/repos.ex b/lib/aura/repos.ex index 9e5c6d2..50497f2 100644 --- a/lib/aura/repos.ex +++ b/lib/aura/repos.ex @@ -1,19 +1,26 @@ defmodule Aura.Repos do @moduledoc false + alias Aura.Model.HexAPIKey alias Aura.Model.HexRepo alias Aura.Requester - def list_repos do - with {:ok, %{body: body}} <- Requester.request(:get, "/repos") do + def list_repos(opts \\ []) do + with {:ok, %{body: body}} <- Requester.get("/repos", opts) do results = Enum.map(body, &HexRepo.build/1) {:ok, results} end end - def get_repo(repo_name) when is_bitstring(repo_name) do - with {:ok, %{body: body}} <- Requester.request(:get, "/repos/#{repo_name}") do + def list_api_keys(opts \\ []) do + with {:ok, %{body: body}} <- Requester.get("/keys", opts) do + {:ok, Enum.map(body, &HexAPIKey.build/1)} + end + end + + def get_repo(repo_name, opts \\ []) when is_bitstring(repo_name) do + with {:ok, %{body: body}} <- Requester.get("/repos/#{repo_name}", opts) do {:ok, HexRepo.build(body)} end end diff --git a/lib/aura/util/requester.ex b/lib/aura/util/requester.ex index 19a79bc..606ae59 100644 --- a/lib/aura/util/requester.ex +++ b/lib/aura/util/requester.ex @@ -9,15 +9,17 @@ defmodule Aura.Requester do def request(method, path, opts \\ []) do qparams = opts[:qparams] is_retry = opts[:is_retry] + repo_url = find_repo_url(opts) opts = opts |> Keyword.delete(:qparams) |> Keyword.delete(:is_retry) + |> Keyword.delete(:repo_url) |> Keyword.put(:headers, [user_agent_header()]) path = - @base_url + repo_url |> Path.join(path) |> handle_qparams(qparams) @@ -42,7 +44,12 @@ defmodule Aura.Requester do {:error, "Rate limit exceeded"} else respect_limits(headers) - new_opts = Keyword.put(opts, :is_retry, true) + + new_opts = + opts + |> Keyword.put(:is_retry, true) + |> Keyword.put(:repo_url, repo_url) + request(method, path, new_opts) end @@ -61,6 +68,12 @@ defmodule Aura.Requester do def delete(path, opts \\ []), do: request(:delete, path, opts) + defp find_repo_url(repo_url: url), do: url + + defp find_repo_url(_) do + Application.get_env(:aura, :repo_url, @base_url) + end + defp user_agent_header do config = Mix.Project.config() app = config[:app] || :unknown diff --git a/mix.exs b/mix.exs index af22c38..7d730c6 100644 --- a/mix.exs +++ b/mix.exs @@ -77,9 +77,9 @@ defmodule Aura.MixProject do defp deps do [ {:ex_doc, "~> 0.37", only: :dev, runtime: false}, - {:ex_license, "~> 0.1.0", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:styler, "~> 1.4", only: [:dev, :test], runtime: false}, + {:ex_doppler, "~> 1.0"}, {:excoveralls, "~> 0.18", only: [:test]}, {:ex_machina, "~> 2.8.0", only: :test}, {:faker, "~> 0.18.0", only: :test}, diff --git a/mix.lock b/mix.lock index ad9d3fd..2540a77 100644 --- a/mix.lock +++ b/mix.lock @@ -6,6 +6,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, + "ex_doppler": {:hex, :ex_doppler, "1.0.1", "f265f505d2faf964332f9a8b83e06f2917e4d601d91e86ffad3a331cd455efab", [:mix], [{:date_time_parser, "~> 1.2.0", [hex: :date_time_parser, repo: "hexpm", optional: false]}, {:ex_license, "~> 0.1.0", [hex: :ex_license, repo: "hexpm", optional: false]}, {:proper_case, "~> 1.3", [hex: :proper_case, repo: "hexpm", optional: false]}, {:req, "~> 0.5.10", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "bfffb5e17f7986708a24be20172135b1f577033bac3789505cf2b934f2822daf"}, "ex_hash_ring": {:hex, :ex_hash_ring, "6.0.4", "bef9d2d796afbbe25ab5b5a7ed746e06b99c76604f558113c273466d52fa6d6b", [:mix], [], "hexpm", "89adabf31f7d3dfaa36802ce598ce918e9b5b33bae8909ac1a4d052e1e567d18"}, "ex_license": {:hex, :ex_license, "0.1.1", "bbdaba704f861894da3ae80e4399984379f19de1cca4ecf7d799616c832b8821", [:mix], [], "hexpm", "dd95aaaba0c9c6f0e9be2db3e9fac0bf69784557e04f700f94e90629f5e24e1d"}, "ex_machina": {:hex, :ex_machina, "2.8.0", "a0e847b5712065055ec3255840e2c78ef9366634d62390839d4880483be38abe", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "79fe1a9c64c0c1c1fab6c4fa5d871682cb90de5885320c187d117004627a7729"}, diff --git a/test/aura/repos_test.exs b/test/aura/repos_test.exs index 14d1370..4c226bb 100644 --- a/test/aura/repos_test.exs +++ b/test/aura/repos_test.exs @@ -5,13 +5,44 @@ defmodule Aura.ReposTest do doctest Repos - test "list_repos/0" do + test "list_repos" do + # use Hex.pm assert {:ok, [%{name: "hexpm"}]} = Repos.list_repos() + + # use another repo URL + mock_repo = TestHelper.get_mock_repo() + assert {:ok, [%{name: "acme"}]} = Repos.list_repos(repo_url: mock_repo) + end + + test "list_api_keys" do + # use another repo + mock_repo = TestHelper.get_mock_repo() + api_key = TestHelper.get_mock_api_key() + Application.put_env(:aura, :api_key, api_key) + Application.put_env(:aura, :repo_url, mock_repo) + + assert {:ok, [api_key]} = Repos.list_api_keys() + assert api_key.authing_key + assert api_key.inserted_at + assert api_key.updated_at + assert api_key.name + assert api_key.url + + Application.delete_env(:aura, :repo_url) + Application.delete_env(:aura, :api_key) end test "get_repo/1" do + # use hex.pm assert {:ok, [hex]} = Repos.list_repos() assert {:ok, returned} = Repos.get_repo(hex.name) assert returned.name == hex.name + + # use another repo + # use another repo URL + mock_repo = TestHelper.get_mock_repo() + assert {:ok, [hex]} = Repos.list_repos(repo_url: mock_repo) + assert {:ok, returned} = Repos.get_repo(hex.name, repo_url: mock_repo) + assert returned.name == hex.name end end diff --git a/test/test_helper.exs b/test/test_helper.exs index 29d7f97..431a59e 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,6 +2,9 @@ ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) {:ok, _} = Application.ensure_all_started(:ex_machina) ExUnit.start(timeout: 2 * 60 * 1000) -defmodule Helper do +defmodule TestHelper do @moduledoc false + + def get_mock_repo, do: ExDoppler.get_secret_raw!("gh", "dev", "MOCK_HEX_REPO") + def get_mock_api_key, do: ExDoppler.get_secret_raw!("gh", "dev", "MOCK_HEX_REPO_KEY") end From 25177aa5ce35cd0c63a37a36e1c85d22f81133b2 Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Tue, 27 May 2025 13:45:12 -0400 Subject: [PATCH 3/4] ci: added secrets --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d1aae7..08be9e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: ci: env: MIX_ENV: test + DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} strategy: fail-fast: false matrix: From 91f6a5c75e29388087b74e243ce4224da49ff7db Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Tue, 27 May 2025 15:53:14 -0400 Subject: [PATCH 4/4] feat: list api keys --- test/aura/model/hex_api_key_test.exs | 16 ++++++++++ test/support/factories/hex_api_key_factory.ex | 29 +++++++++++++++++++ test/support/factory.ex | 1 + 3 files changed, 46 insertions(+) create mode 100644 test/aura/model/hex_api_key_test.exs create mode 100644 test/support/factories/hex_api_key_factory.ex diff --git a/test/aura/model/hex_api_key_test.exs b/test/aura/model/hex_api_key_test.exs new file mode 100644 index 0000000..36b81fc --- /dev/null +++ b/test/aura/model/hex_api_key_test.exs @@ -0,0 +1,16 @@ +defmodule Aura.Model.HexAPIKeyTest do + use ExUnit.Case + + import Aura.Factory + + alias Aura.Model.HexAPIKey + + @moduletag :capture_log + + doctest HexAPIKey + + test "build many" do + list = build_list(100, :hex_api_key) + assert Enum.count(list) == 100 + end +end diff --git a/test/support/factories/hex_api_key_factory.ex b/test/support/factories/hex_api_key_factory.ex new file mode 100644 index 0000000..d14d818 --- /dev/null +++ b/test/support/factories/hex_api_key_factory.ex @@ -0,0 +1,29 @@ +defmodule Aura.Factory.HexAPIKeyFactory do + @moduledoc false + + defmacro __using__(_opts) do + quote do + def hex_api_key_factory(attrs) do + inserted_at = Faker.DateTime.backward(40) + + permissions = [ + %{ + domain: "api", + resource: "read" + } + ] + + %Aura.Model.HexAPIKey{ + authing_key: Enum.random([true, false]), + name: Faker.Internet.slug(), + permissions: permissions, + inserted_at: inserted_at, + url: Faker.Internet.url(), + updated_at: Faker.DateTime.between(inserted_at, DateTime.now!("Etc/UTC")) + } + |> merge_attributes(attrs) + |> evaluate_lazy_attributes() + end + end + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index df44135..b8434ca 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -5,4 +5,5 @@ defmodule Aura.Factory do use Aura.Factory.HexPackageFactory use Aura.Factory.HexPackageOwnerFactory use Aura.Factory.HexReleaseFactory + use Aura.Factory.HexAPIKeyFactory end