From 1adcd9b9a2377a64c6012ae75b6cca3d7b97eed0 Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Mon, 17 Jul 2023 22:11:29 -0400 Subject: [PATCH 1/4] improvement: support setting an actor note to users: this is kind of a hack. We're adding it for edge cases, but its not a full solution. --- .tool-versions | 2 - config/config.exs | 1 + .../tickets/resources/representative.ex | 6 +- .../tickets/resources/ticket/ticket.ex | 2 +- lib/ash_admin/actor_plug.ex | 139 +++++++++++++----- lib/ash_admin/pages/page_live.ex | 36 +---- mix.lock | 46 +++--- 7 files changed, 135 insertions(+), 97 deletions(-) diff --git a/.tool-versions b/.tool-versions index 55e53bf..e69de29 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +0,0 @@ -erlang 25.2.2 -elixir 1.14.3 diff --git a/config/config.exs b/config/config.exs index 1b2a42f..115ae25 100644 --- a/config/config.exs +++ b/config/config.exs @@ -6,6 +6,7 @@ Application.put_env(:ash_admin, Demo.Repo, url: "ecto://#{pg_url}/#{pg_database} config :phoenix, :json_library, Jason config :ash_admin, ecto_repos: [Demo.Repo] +config :ash, :use_all_identities_in_manage_relationship?, false config :surface, :components, [ {Surface.Components.Form.ErrorTag, default_class: "invalid-feedback"} diff --git a/dev/resources/tickets/resources/representative.ex b/dev/resources/tickets/resources/representative.ex index 82f24ff..059267d 100644 --- a/dev/resources/tickets/resources/representative.ex +++ b/dev/resources/tickets/resources/representative.ex @@ -14,10 +14,10 @@ defmodule Demo.Tickets.Representative do resource do base_filter representative: true + end - identities do - identity :representative_name, [:first_name, :last_name] - end + identities do + identity :representative_name, [:first_name, :last_name] end postgres do diff --git a/dev/resources/tickets/resources/ticket/ticket.ex b/dev/resources/tickets/resources/ticket/ticket.ex index 6392313..df8f084 100644 --- a/dev/resources/tickets/resources/ticket/ticket.ex +++ b/dev/resources/tickets/resources/ticket/ticket.ex @@ -92,7 +92,7 @@ defmodule Demo.Tickets.Ticket do update :link do accept [] argument :tickets, {:array, :map}, allow_nil?: false - argument :link_comment, :map, type: :create + argument :link_comment, :map # Uses the defult create action of the join table, which accepts the `type` change manage_relationship(:tickets, :source_links, on_lookup: {:relate_and_update, :create, :read, :all}) diff --git a/lib/ash_admin/actor_plug.ex b/lib/ash_admin/actor_plug.ex index 929e3fd..d09cef5 100644 --- a/lib/ash_admin/actor_plug.ex +++ b/lib/ash_admin/actor_plug.ex @@ -6,6 +6,11 @@ defmodule AshAdmin.ActorPlug do import AshAdmin.Helpers + @plug Application.compile_env(:ash_admin, :actor_plug, __MODULE__) + + @callback set_actor_session(conn :: Plug.Conn.t(), session :: map) :: Plug.Conn.t() + @callback actor_assigns(socket :: Phoenix.LiveView.Socket.t(), session :: map) :: Keyword.t() + def init(opts), do: opts def call(conn, _opts) do @@ -14,49 +19,103 @@ defmodule AshAdmin.ActorPlug do conn session -> - actor_session(conn, session) + set_actor_session(conn, session) + end + end + + if @plug == __MODULE__ do + def actor_assigns(socket, session) do + otp_app = socket.endpoint.config(:otp_app) + apis = apis(otp_app) + + actor_paused = + if is_nil(session["actor_paused"]) do + true + else + session_bool(session["actor_paused"]) + end + + [ + actor: actor_from_session(socket.endpoint, session), + actor_api: actor_api_from_session(socket.endpoint, session), + actor_resources: actor_resources(apis), + authorizing: session_bool(session["actor_authorizing"]) , + actor_paused: actor_paused + ] |> IO.inspect() + end + else + def actor_assigns(socket, session) do + @plug.actor_assigns(socket, session) end end - def actor_session( - conn, + if @plug == __MODULE__ do + defp set_actor_session( + conn, + session + ) do + case session do %{ "actor_resource" => resource, "actor_api" => api, "actor_action" => action, "actor_primary_key" => primary_key - } = session - ) do - authorizing = session["actor_authorizing"] || false - - actor_paused = - if is_nil(session["actor_paused"]) do - true - else - session["actor_paused"] + } -> + authorizing = session["actor_authorizing"] || false + + actor_paused = + if is_nil(session["actor_paused"]) do + true + else + session["actor_paused"] + end + + actor = actor_from_session(conn.private.phoenix_endpoint, session) + + authorizing = session_bool(authorizing) + actor_paused = session_bool(actor_paused) + + conn + |> Plug.Conn.put_session(:actor_resource, resource) + |> Plug.Conn.put_session(:actor_api, api) + |> Plug.Conn.put_session(:actor_action, action) + |> Plug.Conn.put_session(:actor_primary_key, primary_key) + |> Plug.Conn.put_session(:actor_authorizing, authorizing) + |> Plug.Conn.put_session(:actor_paused, actor_paused) + |> Plug.Conn.assign(:actor, actor) + |> Plug.Conn.assign(:authorizing, authorizing || false) + |> Plug.Conn.assign(:actor_paused, actor_paused) + |> Plug.Conn.assign(:authorizing, authorizing) + + _ -> + conn end + end + else + defp set_actor_session(conn, session) do + @plug.set_actor_session(conn, session) + end + end - actor = actor_from_session(conn.private.phoenix_endpoint, session) - - authorizing = session_bool(authorizing) - actor_paused = session_bool(actor_paused) - - conn - |> Plug.Conn.put_session(:actor_resource, resource) - |> Plug.Conn.put_session(:actor_api, api) - |> Plug.Conn.put_session(:actor_action, action) - |> Plug.Conn.put_session(:actor_primary_key, primary_key) - |> Plug.Conn.put_session(:actor_authorizing, authorizing) - |> Plug.Conn.put_session(:actor_paused, actor_paused) - |> Plug.Conn.assign(:actor, actor) - |> Plug.Conn.assign(:authorizing, authorizing || false) - |> Plug.Conn.assign(:actor_paused, actor_paused) - |> Plug.Conn.assign(:authorizing, authorizing) + defp actor_resources(apis) do + apis + |> Enum.flat_map(fn api -> + api + |> Ash.Api.Info.resources() + |> Enum.filter(fn resource -> + AshAdmin.Helpers.primary_action(resource, :read) && AshAdmin.Resource.actor?(resource) + end) + |> Enum.map(fn resource -> {api, resource} end) + end) end - def actor_session(conn, _), do: conn + defp apis(otp_app) do + otp_app + |> Application.get_env(:ash_apis) + |> Enum.filter(&AshAdmin.Api.show?/1) + end - def actor_api_from_session(endpoint, %{"actor_api" => api}) do + defp actor_api_from_session(endpoint, %{"actor_api" => api}) do otp_app = endpoint.config(:otp_app) apis = Application.get_env(otp_app, :ash_apis) @@ -65,15 +124,15 @@ defmodule AshAdmin.ActorPlug do end) end - def actor_api_from_session(_, _), do: nil + defp actor_api_from_session(_, _), do: nil - def actor_from_session(endpoint, %{ - "actor_resource" => resource, - "actor_api" => api, - "actor_primary_key" => primary_key, - "actor_action" => action - }) - when not is_nil(resource) and not is_nil(api) do + defp actor_from_session(endpoint, %{ + "actor_resource" => resource, + "actor_api" => api, + "actor_primary_key" => primary_key, + "actor_action" => action + }) + when not is_nil(resource) and not is_nil(api) do otp_app = endpoint.config(:otp_app) apis = Application.get_env(otp_app, :ash_apis) @@ -109,9 +168,9 @@ defmodule AshAdmin.ActorPlug do end end - def actor_from_session(_, _), do: nil + defp actor_from_session(_, _), do: nil - def session_bool(value) do + defp session_bool(value) do case value do "true" -> true diff --git a/lib/ash_admin/pages/page_live.ex b/lib/ash_admin/pages/page_live.ex index 3a2d299..c64a91e 100644 --- a/lib/ash_admin/pages/page_live.ex +++ b/lib/ash_admin/pages/page_live.ex @@ -40,13 +40,6 @@ defmodule AshAdmin.PageLive do socket = assign(socket, :prefix, prefix) - actor_paused = - if is_nil(session["actor_paused"]) do - true - else - AshAdmin.ActorPlug.session_bool(session["actor_paused"]) - end - apis = apis(otp_app) {:ok, @@ -57,14 +50,13 @@ defmodule AshAdmin.PageLive do |> assign(:record, nil) |> assign(:apis, apis) |> assign(:tenant, session["tenant"]) - |> assign(:actor, AshAdmin.ActorPlug.actor_from_session(socket.endpoint, session)) - |> assign(:actor_api, AshAdmin.ActorPlug.actor_api_from_session(socket.endpoint, session)) - |> assign(:actor_resources, actor_resources(apis)) - |> assign( - :authorizing, - AshAdmin.ActorPlug.session_bool(session["actor_authorizing"]) || false - ) - |> assign(:actor_paused, actor_paused)} + |> then(fn socket -> + assign(socket, AshAdmin.ActorPlug.actor_assigns(socket, session)) + end) + |> assign_new(:actor_api, fn -> nil end) + |> assign_new(:actor_resources, fn -> [] end) + |> assign_new(:authorizing, fn -> true end) + |> assign_new(:actor_paused, fn -> false end)} end @impl true @@ -112,18 +104,6 @@ defmodule AshAdmin.PageLive do """ end - def actor_resources(apis) do - apis - |> Enum.flat_map(fn api -> - api - |> Ash.Api.Info.resources() - |> Enum.filter(fn resource -> - AshAdmin.Helpers.primary_action(resource, :read) && AshAdmin.Resource.actor?(resource) - end) - |> Enum.map(fn resource -> {api, resource} end) - end) - end - defp apis(otp_app) do otp_app |> Application.get_env(:ash_apis) @@ -260,7 +240,7 @@ defmodule AshAdmin.PageLive do case record do {:error, error} -> - Logger.warn( + Logger.warning( "Error while loading record on admin dashboard\n: #{Exception.format(:error, error)}" ) diff --git a/mix.lock b/mix.lock index a5c5473..19627d9 100644 --- a/mix.lock +++ b/mix.lock @@ -1,23 +1,23 @@ %{ - "ash": {:hex, :ash, "2.4.2", "ba579e6654c32b1da49f17938d2f1445066f27e61eedbf0fae431b816b49d1be", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:spark, "~> 0.2 and >= 0.2.7", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da8f94a19cf29617526ca2b1a75f6fae804c1db7c825b49982c603f503a615bd"}, - "ash_phoenix": {:hex, :ash_phoenix, "1.1.0", "7e8da0d463d181f5ee7f029722ea54519947d08d2c154b29b8b88e06d02fabdf", [:mix], [{:ash, "~> 2.0", [hex: :ash, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.15", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "0f8cf588d845fbd827ff522b3102170e4e90a1a2de63743b1be5e37b535a75ec"}, - "ash_postgres": {:hex, :ash_postgres, "1.0.0", "ea90aa6b41d980e654396670e5bf46d91ae3b60381a52b35805aa34e60c4b6e5", [:mix], [{:ash, "~> 2.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "b5108a08b94ed3f2697af32b9a440aae55d0867a2fb00b8abfe84d37d67f3c43"}, + "ash": {:hex, :ash, "2.11.8", "f17d7032abdf7322c19ff32c231d695d6cc69d46c0803860c8cbe28cbbca7ee6", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 1.1.20 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1dffdd3d16f5914b8ba45f2a6cdb9e7e584c4213d9ec25f517a0b91734fff907"}, + "ash_phoenix": {:hex, :ash_phoenix, "1.2.15", "6c417ad9e9d5c3231ce85e3d681b29ef4beb5d677d35207b37f238d161b37ce9", [:mix], [{:ash, ">= 2.5.10 and < 3.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.15", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "4fa35e4ca87c191e14e0ab5d7d80fec1d7d2e1df144d7985aefce30dd5efb33f"}, + "ash_postgres": {:hex, :ash_postgres, "1.3.33", "be433926b70e5c518c67c76624bae51dd7c7a706a52e1e4fad11fd764945f589", [:mix], [{:ash, ">= 2.11.7 and < 3.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "25bcfc1ee16d197100b592163b480db55f17f7ea98352aeb37827368944945c8"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "castore": {:hex, :castore, "1.0.1", "240b9edb4e9e94f8f56ab39d8d2d0a57f49e46c56aced8f873892df8ff64ff5a", [:mix], [], "hexpm", "b4951de93c224d44fac71614beabd88b71932d0b1dea80d2f80fb9044e01bbb3"}, + "castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, - "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, - "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, + "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, "credo": {:hex, :credo, "1.6.4", "ddd474afb6e8c240313f3a7b0d025cc3213f0d171879429bf8535d7021d9ad78", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c28f910b61e1ff829bffa056ef7293a8db50e87f2c57a9b5c3f57eee124536b7"}, - "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, - "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, + "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, + "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, "earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"}, - "ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"}, - "ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"}, - "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, + "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, + "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, + "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"}, "ex_check": {:hex, :ex_check, "0.14.0", "d6fbe0bcc51cf38fea276f5bc2af0c9ae0a2bb059f602f8de88709421dae4f0e", [:mix], [], "hexpm", "8a602e98c66e6a4be3a639321f1f545292042f290f91fa942a285888c6868af0"}, @@ -30,32 +30,32 @@ "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, - "nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"}, + "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "phoenix": {:hex, :phoenix, "1.7.2", "c375ffb482beb4e3d20894f84dd7920442884f5f5b70b9f4528cbe0cedefec63", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1ebca94b32b4d0e097ab2444a9742ed8ff3361acad17365e4e6b2e79b4792159"}, + "phoenix": {:hex, :phoenix, "1.7.7", "4cc501d4d823015007ba3cdd9c41ecaaf2ffb619d6fb283199fa8ddba89191e0", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "8966e15c395e5e37591b6ed0bd2ae7f48e961f0f60ac4c733f9566b519453085"}, "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, - "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, + "phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"}, "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"}, - "picosat_elixir": {:hex, :picosat_elixir, "0.2.2", "1cacfdb4fb0c3ead5e5e9b1e98ac822a777f07eab35e29c3f8fc7086de2bfb36", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9d0cc569552cca417abea8270a54b71153a63be4b951ff249e94642f1c0f35d1"}, + "picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"}, "plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, - "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"}, + "postgrex": {:hex, :postgrex, "0.17.2", "a3ec9e3239d9b33f1e5841565c4eb200055c52cc0757a22b63ca2d529bbe764c", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "80a918a9e9531d39f7bd70621422f3ebc93c01618c645f2d91306f50041ed90c"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"}, - "sourceror": {:hex, :sourceror, "0.12.2", "2ae55efd149193572e0eb723df7c7a1bda9ab33c43373c82642931dbb2f4e428", [:mix], [], "hexpm", "7ad74ade6fb079c71f29fae10c34bcf2323542d8c51ee1bcd77a546cfa89d59c"}, - "spark": {:hex, :spark, "0.2.8", "adce1887be100fae124c65f9dcfb6675fefdcd0ffd6355bb02f638bf88e26630", [:mix], [{:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "1619dc8fd899980e33a7a63939e0a02783ed32c3344d192ff6669bf44ff637a6"}, + "sourceror": {:hex, :sourceror, "0.12.3", "a2ad3a1a4554b486d8a113ae7adad5646f938cad99bf8bfcef26dc0c88e8fade", [:mix], [], "hexpm", "4d4e78010ca046524e8194ffc4683422f34a96f6b82901abbb45acc79ace0316"}, + "spark": {:hex, :spark, "1.1.20", "48f4dd3bed1e16d43a5084a0277f2d8c3adac9bcfa07dfdac9931b025b75480b", [:mix], [{:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "b48d77cdcbc3f00a6e67f447d9860c390f790baf4e7d879d7c6bfd37109c4659"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"}, "surface": {:hex, :surface, "0.10.0", "54eeb1bc7d3e411e3cbd1074237f4369afb24c4e3177ba26f6aaf62336533197", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.18", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.11", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "2cbf3217c1184980a058edc33f9fd8e47c67ebe0c46c2c6448e663eee095dc82"}, @@ -63,6 +63,6 @@ "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "websock": {:hex, :websock, "0.5.0", "f6bbce90226121d62a0715bca7c986c5e43de0ccc9475d79c55381d1796368cc", [:mix], [], "hexpm", "b51ac706df8a7a48a2c622ee02d09d68be8c40418698ffa909d73ae207eb5fb8"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.0", "cea35d8bbf1a6964e32d4b02ceb561dfb769c04f16d60d743885587e7d2ca55b", [:mix], [{:bandit, "~> 0.6", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "16318b124effab8209b1eb7906c636374f623dc9511a8278ad09c083cea5bb83"}, + "websock": {:hex, :websock, "0.5.2", "b3c08511d8d79ed2c2f589ff430bd1fe799bb389686dafce86d28801783d8351", [:mix], [], "hexpm", "925f5de22fca6813dfa980fb62fd542ec43a2d1a1f83d2caec907483fe66ff05"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.3", "4908718e42e4a548fc20e00e70848620a92f11f7a6add8cf0886c4232267498d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "cbe5b814c1f86b6ea002b52dd99f345aeecf1a1a6964e209d208fb404d930d3d"}, } From 1e0d33876ea5e6ab36caaa0225e35f78f8faa125 Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Mon, 17 Jul 2023 22:12:24 -0400 Subject: [PATCH 2/4] chore: remove IO.inspect --- lib/ash_admin/actor_plug.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ash_admin/actor_plug.ex b/lib/ash_admin/actor_plug.ex index d09cef5..0e5c0f1 100644 --- a/lib/ash_admin/actor_plug.ex +++ b/lib/ash_admin/actor_plug.ex @@ -41,7 +41,7 @@ defmodule AshAdmin.ActorPlug do actor_resources: actor_resources(apis), authorizing: session_bool(session["actor_authorizing"]) , actor_paused: actor_paused - ] |> IO.inspect() + ] end else def actor_assigns(socket, session) do From 83d548e44b86f6f09659a21eefc8af63dbe4fad7 Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Mon, 17 Jul 2023 23:05:43 -0400 Subject: [PATCH 3/4] fix: fix behaviour and support on_mount and session --- lib/ash_admin/actor_plug.ex | 181 ++----------------------------- lib/ash_admin/actor_plug/plug.ex | 165 ++++++++++++++++++++++++++++ lib/ash_admin/router.ex | 12 +- 3 files changed, 181 insertions(+), 177 deletions(-) create mode 100644 lib/ash_admin/actor_plug/plug.ex diff --git a/lib/ash_admin/actor_plug.ex b/lib/ash_admin/actor_plug.ex index 0e5c0f1..db414d4 100644 --- a/lib/ash_admin/actor_plug.ex +++ b/lib/ash_admin/actor_plug.ex @@ -2,190 +2,23 @@ defmodule AshAdmin.ActorPlug do @moduledoc false @behaviour Plug - require Ash.Query - import AshAdmin.Helpers + @plug Application.compile_env(:ash_admin, :actor_plug, AshAdmin.ActorPlug.Plug) - @plug Application.compile_env(:ash_admin, :actor_plug, __MODULE__) - - @callback set_actor_session(conn :: Plug.Conn.t(), session :: map) :: Plug.Conn.t() + @callback set_actor_session(conn :: Plug.Conn.t()) :: Plug.Conn.t() @callback actor_assigns(socket :: Phoenix.LiveView.Socket.t(), session :: map) :: Keyword.t() def init(opts), do: opts def call(conn, _opts) do - case conn.cookies do - %{"actor_resource" => "undefined"} -> - conn - - session -> - set_actor_session(conn, session) - end - end - - if @plug == __MODULE__ do - def actor_assigns(socket, session) do - otp_app = socket.endpoint.config(:otp_app) - apis = apis(otp_app) - - actor_paused = - if is_nil(session["actor_paused"]) do - true - else - session_bool(session["actor_paused"]) - end - - [ - actor: actor_from_session(socket.endpoint, session), - actor_api: actor_api_from_session(socket.endpoint, session), - actor_resources: actor_resources(apis), - authorizing: session_bool(session["actor_authorizing"]) , - actor_paused: actor_paused - ] - end - else - def actor_assigns(socket, session) do - @plug.actor_assigns(socket, session) - end - end - - if @plug == __MODULE__ do - defp set_actor_session( - conn, - session - ) do - case session do - %{ - "actor_resource" => resource, - "actor_api" => api, - "actor_action" => action, - "actor_primary_key" => primary_key - } -> - authorizing = session["actor_authorizing"] || false - - actor_paused = - if is_nil(session["actor_paused"]) do - true - else - session["actor_paused"] - end - - actor = actor_from_session(conn.private.phoenix_endpoint, session) - - authorizing = session_bool(authorizing) - actor_paused = session_bool(actor_paused) - - conn - |> Plug.Conn.put_session(:actor_resource, resource) - |> Plug.Conn.put_session(:actor_api, api) - |> Plug.Conn.put_session(:actor_action, action) - |> Plug.Conn.put_session(:actor_primary_key, primary_key) - |> Plug.Conn.put_session(:actor_authorizing, authorizing) - |> Plug.Conn.put_session(:actor_paused, actor_paused) - |> Plug.Conn.assign(:actor, actor) - |> Plug.Conn.assign(:authorizing, authorizing || false) - |> Plug.Conn.assign(:actor_paused, actor_paused) - |> Plug.Conn.assign(:authorizing, authorizing) - - _ -> - conn - end - end - else - defp set_actor_session(conn, session) do - @plug.set_actor_session(conn, session) - end - end - - defp actor_resources(apis) do - apis - |> Enum.flat_map(fn api -> - api - |> Ash.Api.Info.resources() - |> Enum.filter(fn resource -> - AshAdmin.Helpers.primary_action(resource, :read) && AshAdmin.Resource.actor?(resource) - end) - |> Enum.map(fn resource -> {api, resource} end) - end) + set_actor_session(conn) end - defp apis(otp_app) do - otp_app - |> Application.get_env(:ash_apis) - |> Enum.filter(&AshAdmin.Api.show?/1) + def actor_assigns(socket, session) do + @plug.actor_assigns(socket, session) end - defp actor_api_from_session(endpoint, %{"actor_api" => api}) do - otp_app = endpoint.config(:otp_app) - apis = Application.get_env(otp_app, :ash_apis) - - Enum.find(apis, fn allowed_api -> - AshAdmin.Api.show?(allowed_api) && AshAdmin.Api.name(allowed_api) == api - end) - end - - defp actor_api_from_session(_, _), do: nil - - defp actor_from_session(endpoint, %{ - "actor_resource" => resource, - "actor_api" => api, - "actor_primary_key" => primary_key, - "actor_action" => action - }) - when not is_nil(resource) and not is_nil(api) do - otp_app = endpoint.config(:otp_app) - apis = Application.get_env(otp_app, :ash_apis) - - api = - Enum.find(apis, fn allowed_api -> - AshAdmin.Api.show?(allowed_api) && AshAdmin.Api.name(allowed_api) == api - end) - - resource = - if api do - api - |> Ash.Api.Info.resources() - |> Enum.find(fn api_resource -> - AshAdmin.Resource.name(api_resource) == resource - end) - end - - if api && resource do - action = - if action do - Ash.Resource.Info.action(resource, String.to_existing_atom(action), :read) - end - - case decode_primary_key(resource, primary_key) do - :error -> - nil - - {:ok, filter} -> - resource - |> Ash.Query.filter(^filter) - |> api.read_one!(action: action, authorize?: false) - end - end - end - - defp actor_from_session(_, _), do: nil - - defp session_bool(value) do - case value do - "true" -> - true - - "false" -> - false - - "undefined" -> - false - - boolean when is_boolean(boolean) -> - boolean - - nil -> - false - end + def set_actor_session(conn) do + @plug.set_actor_session(conn) end end diff --git a/lib/ash_admin/actor_plug/plug.ex b/lib/ash_admin/actor_plug/plug.ex new file mode 100644 index 0000000..c48b970 --- /dev/null +++ b/lib/ash_admin/actor_plug/plug.ex @@ -0,0 +1,165 @@ +defmodule AshAdmin.ActorPlug.Plug do + @behaviour AshAdmin.ActorPlug + import AshAdmin.Helpers + require Ash.Query + + @impl true + def actor_assigns(socket, session) do + otp_app = socket.endpoint.config(:otp_app) + apis = apis(otp_app) + + actor_paused = + if is_nil(session["actor_paused"]) do + true + else + session_bool(session["actor_paused"]) + end + + [ + actor: actor_from_session(socket.endpoint, session), + actor_api: actor_api_from_session(socket.endpoint, session), + actor_resources: actor_resources(apis), + authorizing: session_bool(session["actor_authorizing"]), + actor_paused: actor_paused + ] + end + + @impl true + def set_actor_session(conn) do + case conn.cookies do + %{"actor_resource" => "undefined"} -> + conn + + session -> + case session do + %{ + "actor_resource" => resource, + "actor_api" => api, + "actor_action" => action, + "actor_primary_key" => primary_key + } -> + authorizing = session["actor_authorizing"] || false + + actor_paused = + if is_nil(session["actor_paused"]) do + true + else + session["actor_paused"] + end + + actor = actor_from_session(conn.private.phoenix_endpoint, session) + + authorizing = session_bool(authorizing) + actor_paused = session_bool(actor_paused) + + conn + |> Plug.Conn.put_session(:actor_resource, resource) + |> Plug.Conn.put_session(:actor_api, api) + |> Plug.Conn.put_session(:actor_action, action) + |> Plug.Conn.put_session(:actor_primary_key, primary_key) + |> Plug.Conn.put_session(:actor_authorizing, authorizing) + |> Plug.Conn.put_session(:actor_paused, actor_paused) + |> Plug.Conn.assign(:actor, actor) + |> Plug.Conn.assign(:authorizing, authorizing || false) + |> Plug.Conn.assign(:actor_paused, actor_paused) + |> Plug.Conn.assign(:authorizing, authorizing) + + _ -> + conn + end + end + end + + defp session_bool(value) do + case value do + "true" -> + true + + "false" -> + false + + "undefined" -> + false + + boolean when is_boolean(boolean) -> + boolean + + nil -> + false + end + end + + defp actor_resources(apis) do + apis + |> Enum.flat_map(fn api -> + api + |> Ash.Api.Info.resources() + |> Enum.filter(fn resource -> + AshAdmin.Helpers.primary_action(resource, :read) && AshAdmin.Resource.actor?(resource) + end) + |> Enum.map(fn resource -> {api, resource} end) + end) + end + + defp apis(otp_app) do + otp_app + |> Application.get_env(:ash_apis) + |> Enum.filter(&AshAdmin.Api.show?/1) + end + + defp actor_api_from_session(endpoint, %{"actor_api" => api}) do + otp_app = endpoint.config(:otp_app) + apis = Application.get_env(otp_app, :ash_apis) + + Enum.find(apis, fn allowed_api -> + AshAdmin.Api.show?(allowed_api) && AshAdmin.Api.name(allowed_api) == api + end) + end + + defp actor_api_from_session(_, _), do: nil + + defp actor_from_session(endpoint, %{ + "actor_resource" => resource, + "actor_api" => api, + "actor_primary_key" => primary_key, + "actor_action" => action + }) + when not is_nil(resource) and not is_nil(api) do + otp_app = endpoint.config(:otp_app) + apis = Application.get_env(otp_app, :ash_apis) + + api = + Enum.find(apis, fn allowed_api -> + AshAdmin.Api.show?(allowed_api) && AshAdmin.Api.name(allowed_api) == api + end) + + resource = + if api do + api + |> Ash.Api.Info.resources() + |> Enum.find(fn api_resource -> + AshAdmin.Resource.name(api_resource) == resource + end) + end + + if api && resource do + action = + if action do + Ash.Resource.Info.action(resource, String.to_existing_atom(action), :read) + end + + case decode_primary_key(resource, primary_key) do + :error -> + nil + + {:ok, filter} -> + resource + |> Ash.Query.filter(^filter) + |> api.read_one!(action: action, authorize?: false) + end + end + end + + defp actor_from_session(_, _), do: nil + +end diff --git a/lib/ash_admin/router.ex b/lib/ash_admin/router.ex index 532c15b..184bd42 100644 --- a/lib/ash_admin/router.ex +++ b/lib/ash_admin/router.ex @@ -64,7 +64,8 @@ defmodule AshAdmin.Router do live_socket_path = Keyword.get(opts, :live_socket_path, "/live") live_session :ash_admin, - session: {AshAdmin.Router, :__session__, [%{"prefix" => path}]}, + on_mount: List.wrap(opts[:on_mount]), + session: [{AshAdmin.Router, :__session__, [%{"prefix" => path}, List.wrap(opts[:session])]}], root_layout: {AshAdmin.LayoutView, :root} do live( "#{path}/*route", @@ -87,9 +88,14 @@ defmodule AshAdmin.Router do ] @doc false - def __session__(conn, [session]), do: __session__(conn, session) + def __session__(conn, [session, additional_hooks]), do: __session__(conn, session, additional_hooks) + + def __session__(conn, session, additional_hooks \\ []) do + session = + Enum.reduce(additional_hooks, session, fn {m, f, a}, acc -> + Map.merge(acc, apply(m, f, [conn | a]) || %{}) + end) - def __session__(conn, session) do session = Map.put(session, "request_path", conn.request_path) Enum.reduce(@cookies_to_replicate, session, fn cookie, session -> From 26672093c5c4b6730cf57ac458b57075031a4f56 Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Mon, 17 Jul 2023 23:10:35 -0400 Subject: [PATCH 4/4] chore: fix `session` option --- lib/ash_admin/router.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ash_admin/router.ex b/lib/ash_admin/router.ex index 184bd42..2577de5 100644 --- a/lib/ash_admin/router.ex +++ b/lib/ash_admin/router.ex @@ -65,7 +65,7 @@ defmodule AshAdmin.Router do live_session :ash_admin, on_mount: List.wrap(opts[:on_mount]), - session: [{AshAdmin.Router, :__session__, [%{"prefix" => path}, List.wrap(opts[:session])]}], + session: {AshAdmin.Router, :__session__, [%{"prefix" => path}, List.wrap(opts[:session])]}, root_layout: {AshAdmin.LayoutView, :root} do live( "#{path}/*route",