diff --git a/.formatter.exs b/.formatter.exs index 8fd6d92..a490e2e 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -8,5 +8,5 @@ assert_style: 1, assert_style: 2 ], - plugins: [Quokka] + plugins: [Styler] ] diff --git a/lib/aura/model/common.ex b/lib/aura/model/common.ex index 82e9f1a..76bc1c3 100644 --- a/lib/aura/model/common.ex +++ b/lib/aura/model/common.ex @@ -3,12 +3,9 @@ defmodule Aura.Model.Common do @spec prepare(m :: map()) :: map() def prepare(m) when is_map(m) do - prepare_keys(m) + m + |> prepare_keys() |> prepare_values() - |> Enum.reject(fn {_k, v} -> v == nil end) - |> Enum.map(fn {key, val} -> - {key, serialize(key, val)} - end) end defp prepare_keys(m) do @@ -37,6 +34,4 @@ defmodule Aura.Model.Common do {key, val} end) end - - defp serialize(_k, v), do: v end diff --git a/lib/aura/model/hex_package.ex b/lib/aura/model/hex_package.ex new file mode 100644 index 0000000..959150d --- /dev/null +++ b/lib/aura/model/hex_package.ex @@ -0,0 +1,93 @@ +defmodule Aura.Model.HexPackage do + @moduledoc false + + import Aura.Model.Common + + alias Aura.Model.DownloadStats + alias Aura.Model.HexPackage + alias Aura.Model.PackageMeta + alias Aura.Model.Release + + DownloadStats + + defstruct [ + :name, + :repository, + :private, + :meta, + :downloads, + :releases, + :inserted_at, + :updated_at, + :url, + :html_url, + :docs_html_url + ] + + def build(m) when is_map(m) do + fields = + m + |> prepare() + |> Enum.map(fn {k, v} -> {k, serialize(k, v)} end) + + struct(HexPackage, fields) + end + + defp serialize(:meta, v), do: PackageMeta.build(v) + defp serialize(:downloads, v), do: DownloadStats.build(v) + + defp serialize(:releases, v), do: Enum.map(v, &Release.build/1) + + defp serialize(_k, v), do: v +end + +defmodule Aura.Model.PackageMeta do + @moduledoc false + + import Aura.Model.Common + + defstruct [ + # + :maintainers, + :links, + :licenses, + :description + ] + + def build(m) when is_map(m) do + fields = prepare(m) + + struct(Aura.Model.PackageMeta, fields) + end +end + +defmodule Aura.Model.DownloadStats do + @moduledoc false + + import Aura.Model.Common + + defstruct all: 0, + week: 0, + day: 0 + + def build(m) when is_map(m) do + fields = prepare(m) + struct(Aura.Model.DownloadStats, fields) + end +end + +defmodule Aura.Model.Release do + @moduledoc false + + import Aura.Model.Common + + defstruct [ + :version, + :url + ] + + def build(m) when is_map(m) do + fields = prepare(m) + struct(Aura.Model.Release, fields) + end +end diff --git a/lib/aura/model/hex_repo.ex b/lib/aura/model/hex_repo.ex index 7cc26c5..be41f51 100644 --- a/lib/aura/model/hex_repo.ex +++ b/lib/aura/model/hex_repo.ex @@ -15,9 +15,7 @@ defmodule Aura.Model.HexRepo do ] def build(m) when is_map(m) do - fields = - m - |> prepare() + fields = prepare(m) struct(HexRepo, fields) end diff --git a/lib/aura/packages.ex b/lib/aura/packages.ex new file mode 100644 index 0000000..b97357c --- /dev/null +++ b/lib/aura/packages.ex @@ -0,0 +1,54 @@ +defmodule Aura.Packages do + @moduledoc false + + alias Aura.Model.HexPackage + alias Aura.Requester + + def list_packages(opts \\ []) do + stream_paginate("/packages", opts) + end + + def get_package(name) do + with {:ok, %{body: body}} <- Requester.get("/packages/#{name}") do + {:ok, HexPackage.build(body)} + end + end + + defp stream_paginate(path, opts) do + qparams = + Keyword.merge([page: 1], opts) + + start_fun = fn -> max(1, qparams[:page]) end + end_fun = fn _ -> :ok end + + continue_fun = &paginate_with_page(&1, path, qparams) + + Stream.resource(start_fun, continue_fun, end_fun) + end + + defp paginate_with_page(page, _path, _qparams) when page < 1 do + {:halt, page} + end + + defp paginate_with_page(page, path, qparams) when page >= 1 do + path + |> get_package_page(page, qparams) + |> case do + {:ok, package_page} -> + packages = Enum.map(package_page, &HexPackage.build/1) + next_page = if Enum.empty?(packages), do: -1, else: page + 1 + {packages, next_page} + + e -> + {:halt, {:error, e}} + end + end + + defp get_package_page(path, page, qparams) do + qparams = Keyword.put(qparams, :page, page) + + with {:ok, %{body: body}} <- Aura.Requester.get(path, qparams: qparams) do + {:ok, body} + end + end +end diff --git a/lib/aura/repos.ex b/lib/aura/repos.ex index 55da55a..9e5c6d2 100644 --- a/lib/aura/repos.ex +++ b/lib/aura/repos.ex @@ -4,11 +4,9 @@ defmodule Aura.Repos do alias Aura.Model.HexRepo alias Aura.Requester - def get_all_repos do + def list_repos do with {:ok, %{body: body}} <- Requester.request(:get, "/repos") do - results = - body - |> Enum.map(&HexRepo.build/1) + results = Enum.map(body, &HexRepo.build/1) {:ok, results} end diff --git a/lib/aura/util/requester.ex b/lib/aura/util/requester.ex index 3cde0e1..d390dff 100644 --- a/lib/aura/util/requester.ex +++ b/lib/aura/util/requester.ex @@ -72,7 +72,7 @@ defmodule Aura.Requester do end defp otp_version do - major = :erlang.system_info(:otp_release) |> List.to_string() + major = :otp_release |> :erlang.system_info() |> List.to_string() vsn_file = Path.join([:code.root_dir(), "releases", major, "OTP_VERSION"]) try do diff --git a/mix.exs b/mix.exs index 938e049..af22c38 100644 --- a/mix.exs +++ b/mix.exs @@ -79,7 +79,7 @@ defmodule Aura.MixProject 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}, - {:quokka, "~> 2.6", only: [:dev, :test], runtime: false}, + {:styler, "~> 1.4", only: [:dev, :test], runtime: false}, {: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 2635530..83b7525 100644 --- a/mix.lock +++ b/mix.lock @@ -30,6 +30,7 @@ "quokka": {:hex, :quokka, "2.6.0", "a34ff876968fcef20594e349bd7d9a940c0ba85830d0efd8416ee8c0de4c430d", [:mix], [{:credo, "~> 1.7", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm", "52ea62131cef122e2daddbb3b9f61b14d04adcb39316fe0dae03ca65eeec270b"}, "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"}, + "styler": {:hex, :styler, "1.4.2", "420da8a9d10324625b75690ca9f2468bc00ee6eb78dead827e562368f9feabbb", [:mix], [], "hexpm", "ca22538b203b2424eef99a227e081143b9a9a4b26da75f26d920537fcd778832"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"}, } diff --git a/test/aura/packages_test.exs b/test/aura/packages_test.exs new file mode 100644 index 0000000..3934919 --- /dev/null +++ b/test/aura/packages_test.exs @@ -0,0 +1,45 @@ +defmodule Aura.PackagesTest do + @moduledoc false + + use ExUnit.Case + + alias Aura.Packages + + doctest Aura.Packages + + test "list_packages" do + # First 2000 + first_2k = Enum.take(Packages.list_packages(), 2000) + assert Enum.count(first_2k) == 2000 + + Enum.each(first_2k, fn package -> + assert package.name + assert package.repository + assert package.url + assert package.meta + assert package.downloads + assert package.releases + end) + + # Page 25 onward + page_25_forward = + [page: 25] + |> Packages.list_packages() + |> Enum.take(200) + + assert Enum.count(page_25_forward) == 200 + + Enum.each(page_25_forward, fn package -> + assert package.name + assert package.repository + assert package.url + refute Enum.member?(first_2k, package) + end) + end + + test "get_package" do + [package | _] = Enum.take(Packages.list_packages(), 1) + assert {:ok, retrieved} = Packages.get_package(package.name) + assert package == retrieved + end +end diff --git a/test/aura/repos_test.exs b/test/aura/repos_test.exs index 878edc4..14d1370 100644 --- a/test/aura/repos_test.exs +++ b/test/aura/repos_test.exs @@ -5,12 +5,12 @@ defmodule Aura.ReposTest do doctest Repos - test "get_all_repos/0" do - assert {:ok, [%{name: "hexpm"}]} = Repos.get_all_repos() + test "list_repos/0" do + assert {:ok, [%{name: "hexpm"}]} = Repos.list_repos() end test "get_repo/1" do - assert {:ok, [hex]} = Repos.get_all_repos() + assert {:ok, [hex]} = Repos.list_repos() assert {:ok, returned} = Repos.get_repo(hex.name) assert returned.name == hex.name end