From d452e925a460df042099cb1e10b4ce26c733db27 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Thu, 1 Oct 2026 12:44:39 +0400 Subject: [PATCH] Leave unknown names out of fetch_icons instead of raising fetch_icons/2 called Set.get! for every requested name, so fetch_icon/2 raised for an unknown icon instead of returning {:error, :not_found}. Also depend on the published json_codec 0.3 instead of a local path and drop its deprecated fast_path option. E2E tests against the Iconify API run with mix test.e2e. --- CHANGELOG.md | 5 +++++ lib/iconify/fetcher.ex | 5 ++--- lib/iconify/icon.ex | 2 +- lib/iconify/set.ex | 2 +- mix.exs | 5 +++-- mix.lock | 2 ++ test/e2e/iconify/fetcher_test.exs | 24 ++++++++++++++++++++++++ test/test_helper.exs | 2 +- 8 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 test/e2e/iconify/fetcher_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee4053..8042182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- Leave unknown names out of `Iconify.Fetcher.fetch_icons/2` instead of raising, so `fetch_icon/2` returns `{:error, :not_found}` for them as documented. +- Parse icon sets and icons with JSONCodec 0.3. + ## v0.3.0 - Replace SVG IDs during rendering with Erlang's `:xmerl` to avoid duplicate ID collisions diff --git a/lib/iconify/fetcher.ex b/lib/iconify/fetcher.ex index 45f721c..0f0141c 100644 --- a/lib/iconify/fetcher.ex +++ b/lib/iconify/fetcher.ex @@ -44,7 +44,7 @@ defmodule Iconify.Fetcher do @doc """ Fetches specific icons from the Iconify API. - Returns a map of icon name to Icon struct. + Returns a map of icon name to Icon struct. Names the set doesn't have are left out. ## Examples @@ -60,8 +60,7 @@ defmodule Iconify.Fetcher do with {:ok, json} <- req_get_body(url), {:ok, set} <- Set.parse(json) do - icons = Map.new(names, fn name -> {name, Set.get!(set, name)} end) - {:ok, icons} + {:ok, for(name <- names, {:ok, icon} <- [Set.get(set, name)], into: %{}, do: {name, icon})} end end diff --git a/lib/iconify/icon.ex b/lib/iconify/icon.ex index a8e6b0e..15e5cea 100644 --- a/lib/iconify/icon.ex +++ b/lib/iconify/icon.ex @@ -3,7 +3,7 @@ defmodule Iconify.Icon do Represents a single normalized Iconify icon. """ - use JSONCodec, case: :camel, fast_path: :json + use JSONCodec, case: :camel @derive Jason.Encoder defstruct [ diff --git a/lib/iconify/set.ex b/lib/iconify/set.ex index 522c389..f235ecb 100644 --- a/lib/iconify/set.ex +++ b/lib/iconify/set.ex @@ -3,7 +3,7 @@ defmodule Iconify.Set do Represents an Iconify icon set (collection of icons with a common prefix). """ - use JSONCodec, case: :camel, fast_path: :json + use JSONCodec, case: :camel alias Iconify.Icon diff --git a/mix.exs b/mix.exs index c8e1576..1ad5023 100644 --- a/mix.exs +++ b/mix.exs @@ -28,7 +28,7 @@ defmodule Iconify.MixProject do defp deps do [ {:jason, "~> 1.4"}, - {:json_codec, path: "../json_codec"}, + {:json_codec, "~> 0.3"}, {:req, "~> 0.5"}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:reach, "~> 2.0", only: [:dev, :test], runtime: false}, @@ -38,11 +38,12 @@ defmodule Iconify.MixProject do end def cli do - [preferred_envs: [ci: :test]] + [preferred_envs: [ci: :test, "test.e2e": :test]] end defp aliases do [ + "test.e2e": ["test --only e2e"], ci: [ "compile --warnings-as-errors", "format --check-formatted", diff --git a/mix.lock b/mix.lock index e9b440d..0dffaf9 100644 --- a/mix.lock +++ b/mix.lock @@ -9,6 +9,8 @@ "finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"}, "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "json_codec": {:hex, :json_codec, "0.3.1", "81f738e212308c85344e11da00af6d8b52bd486224ee24c1f51b95df0604aeae", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:json_spec, "~> 1.2", [hex: :json_spec, repo: "hexpm", optional: false]}], "hexpm", "dffea3198d3f1fe9a39aa176ab0d6988b8c80dd85a67e913b323d4ec7c57ca9d"}, + "json_spec": {:hex, :json_spec, "1.2.0", "c67f73cdc4166e955b2f1095d3f44dd78c197337038f1a5301fb526b6c87ab04", [:mix], [], "hexpm", "3639961babad7d6bc45f3a2d96a49c18ec70614761bb84ce58b64e9fad9354de"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, diff --git a/test/e2e/iconify/fetcher_test.exs b/test/e2e/iconify/fetcher_test.exs new file mode 100644 index 0000000..506f55c --- /dev/null +++ b/test/e2e/iconify/fetcher_test.exs @@ -0,0 +1,24 @@ +defmodule Iconify.E2E.FetcherTest do + # Talks to the Iconify API. Run with `mix test.e2e`. + use ExUnit.Case, async: true + + alias Iconify.Fetcher + + @moduletag :e2e + + setup do + {:ok, _} = Application.ensure_all_started(:req) + :ok + end + + test "fetch_icons/2 leaves out names the set doesn't have" do + assert {:ok, icons} = Fetcher.fetch_icons("lucide", ["mail", "no-such-icon-anywhere"]) + assert Map.keys(icons) == ["mail"] + assert icons["mail"].body =~ "<" + end + + test "fetch_icon/2 reports an unknown name as not found" do + assert {:ok, %Iconify.Icon{}} = Fetcher.fetch_icon("lucide", "layer-arrow-up") + assert Fetcher.fetch_icon("lucide", "no-such-icon-anywhere") == {:error, :not_found} + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 869559e..8965455 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1 @@ -ExUnit.start() +ExUnit.start(exclude: [:e2e])