improvement: upgrade to 3.0

This commit is contained in:
Zach Daniel 2024-03-29 09:48:44 -04:00
parent 7e303883ee
commit 657d36d3e2
42 changed files with 381 additions and 362 deletions

View file

@ -1,10 +1,9 @@
---
name: Proposal
about: Suggest an idea for this project
title: ''
title: ""
labels: enhancement, needs review
assignees: ''
assignees: ""
---
**Is your feature request related to a problem? Please describe.**
@ -29,7 +28,7 @@ For example
Or
```elixir
Api.read(:resource, bar: 10) # <- Adding `bar` here would cause <x>
Ash.read(:resource, bar: 10) # <- Adding `bar` here would cause <x>
```
**Additional context**

View file

@ -21,29 +21,30 @@ First, ensure you've added ash_admin to your `mix.exs` file.
## Setup
Ensure your apis are configured in `config.exs`
Ensure your domains are configured in `config.exs`
```elixir
config :my_app, ash_apis: [MyApp.Foo, MyApp.Bar]
config :my_app, ash_domains: [MyApp.Foo, MyApp.Bar]
```
Add the admin extension to each api you want to show in AshAdmin dashboard, and configure it to show. See [`AshAdmin.Api`](https://hexdocs.pm/ash_admin/AshAdmin.Api.html) for more configuration options.
Add the admin extension to each domain you want to show in AshAdmin dashboard, and configure it to show. See [`AshAdmin.Domain`](https://hexdocs.pm/ash_admin/AshAdmin.Domain.html) for more configuration options.
```elixir
# In your Api(s)
use Ash.Api,
extensions: [AshAdmin.Api]
# In your Domain(s)
use Ash.Domain,
extensions: [AshAdmin.Domain]
admin do
show? true
end
```
Resources in each Api will be automagically included in AshAdmin. See [`AshAdmin.Resource`](https://hexdocs.pm/ash_admin/AshAdmin.Resource.html) for more resource configuration options. Specifically, if you app has an actor you will want to configure that. Ash Admin allows you to change actors and therefore doesn't rely on `Ash.set_actor`
Resources in each Domain will be included in AshAdmin. See [`AshAdmin.Resource`](https://hexdocs.pm/ash_admin/AshAdmin.Resource.html) for more resource configuration options. Specifically, if you app has an actor you will want to configure that. Ash Admin allows you to change actors and therefore doesn't rely on `Ash.set_actor`
```elixir
# In your resource that acts as an actor (e.g. User)
use Ash.Resource,
domain: YourDomain,
extensions: [AshAdmin.Resource]
admin do
@ -103,7 +104,7 @@ This will allow AshAdmin-generated inline CSS and JS blocks to execute normally.
## Configuration
See the documentation in [`AshAdmin.Resource`](https://hexdocs.pm/ash_admin/AshAdmin.Resource.html) and [`AshAdmin.Api`](https://hexdocs.pm/ash_admin/AshAdmin.Api.html) for information on the available configuration.
See the documentation in [`AshAdmin.Resource`](https://hexdocs.pm/ash_admin/AshAdmin.Resource.html) and [`AshAdmin.Domain`](https://hexdocs.pm/ash_admin/AshAdmin.Domain.html) for information on the available configuration.
## Troubleshooting

View file

@ -40,7 +40,7 @@ Hooks.JsonEditor = {
},
modes: ["text", "tree"],
},
JSON.parse(document.getElementById(inputId).value)
JSON.parse(document.getElementById(inputId).value),
);
editors[this.el.id] = this.editor;
@ -71,7 +71,7 @@ Hooks.JsonView = {
{
mode: "preview",
},
json
json,
);
},
mounted() {
@ -81,7 +81,7 @@ Hooks.JsonView = {
{
mode: "preview",
},
json
json,
);
},
};
@ -111,13 +111,13 @@ Hooks.Actor = {
document.cookie =
"actor_primary_key" + "=" + payload.primary_key + ";path=/";
document.cookie = "actor_action" + "=" + payload.action + ";path=/";
document.cookie = "actor_api" + "=" + payload.api + ";path=/";
document.cookie = "actor_domain" + "=" + payload.domain + ";path=/";
});
this.handleEvent("clear_actor", () => {
document.cookie = "actor_resource" + "=" + ";path=/";
document.cookie = "actor_primary_key" + "=" + ";path=/";
document.cookie = "actor_action" + ";path=/";
document.cookie = "actor_api" + "=" + ";path=/";
document.cookie = "actor_domain" + "=" + ";path=/";
document.cookie = "actor_authorizing=false;path=/";
document.cookie = "actor_paused=true;path=/";
});
@ -160,22 +160,21 @@ Hooks.MaintainAttrs = {
function getCookie(name) {
var re = new RegExp(name + "=([^;]+)");
var value = re.exec(document.cookie);
return (value != null) ? unescape(value[1]) : null;
};
return value != null ? unescape(value[1]) : null;
}
let params = () => {
return {
_csrf_token: csrfToken,
_csrf_token: csrfToken,
tenant: getCookie("tenant"),
actor_resource: getCookie("actor_resource"),
actor_primary_key: getCookie("actor_primary_key"),
actor_action: getCookie("actor_action"),
actor_api: getCookie("actor_api"),
actor_domain: getCookie("actor_domain"),
actor_authorizing: getCookie("actor_authorizing"),
actor_paused: getCookie("actor_paused")
}
}
actor_paused: getCookie("actor_paused"),
};
};
let liveSocket = new LiveSocket(socketPath, Socket, {
params: params,

View file

@ -44,9 +44,9 @@ config :phoenix, :serve_endpoints, true
if config_env() == :dev do
config :ash_admin,
ash_apis: [
Demo.Accounts.Api,
Demo.Tickets.Api
ash_domains: [
Demo.Accounts.Domain,
Demo.Tickets.Domain
]
config :git_ops,
@ -73,7 +73,7 @@ if config_env() == :test do
config :ash, :disable_async?, true
config :ash_admin,
ash_apis: [
AshAdmin.Test.Api
ash_domains: [
AshAdmin.Test.Domain
]
end

View file

@ -1,13 +0,0 @@
defmodule Demo.Accounts.Api do
@moduledoc false
use Ash.Api,
extensions: [AshAdmin.Api]
admin do
show? true
end
resources do
registry Demo.Accounts.Registry
end
end

View file

@ -0,0 +1,13 @@
defmodule Demo.Accounts.Domain do
@moduledoc false
use Ash.Domain,
extensions: [AshAdmin.Domain]
admin do
show? true
end
resources do
resource Demo.Accounts.User
end
end

View file

@ -1,7 +0,0 @@
defmodule Demo.Accounts.Registry do
use Ash.Registry
entries do
entry Demo.Accounts.User
end
end

View file

@ -1,16 +1,8 @@
defmodule Demo.Accounts.NestedEmbed do
use Ash.Resource,
data_layer: :embedded,
extensions: [AshAdmin.Resource]
admin do
form do
field :bio, type: :long_text
field :history, type: :long_text
end
end
data_layer: :embedded
attributes do
attribute :tags, {:array, :string}, default: []
attribute :tags, {:array, :string}, default: [], public?: true
end
end

View file

@ -11,10 +11,10 @@ defmodule Demo.Accounts.Profile do
end
attributes do
attribute :bio, :string, allow_nil?: false
attribute :history, :string
attribute :tags, {:array, :string}, default: []
attribute :metadata, :map
attribute :nested_embed, Demo.Accounts.NestedEmbed
attribute :bio, :string, allow_nil?: false, public?: true
attribute :history, :string, public?: true
attribute :tags, {:array, :string}, default: [], public?: true
attribute :metadata, :map, public?: true
attribute :nested_embed, Demo.Accounts.NestedEmbed, public?: true
end
end

View file

@ -1,5 +1,6 @@
defmodule Demo.Accounts.User do
use Ash.Resource,
domain: Demo.Accounts.Domain,
data_layer: AshPostgres.DataLayer,
authorizers: [
Ash.Policy.Authorizer
@ -38,6 +39,7 @@ defmodule Demo.Accounts.User do
end
actions do
default_accept :*
read :me, filter: [id: actor(:id)]
read :read, primary?: true
read :by_id do
@ -80,16 +82,21 @@ defmodule Demo.Accounts.User do
attribute :first_name, :string do
constraints min_length: 1
public? true
end
attribute :last_name, :string do
constraints min_length: 1
public? true
end
attribute :metadata, :map
attribute :metadata, :map do
public? true
end
attribute :representative, :boolean do
allow_nil? false
public? true
default false
description """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
@ -102,30 +109,39 @@ defmodule Demo.Accounts.User do
end
attribute :admin, :boolean do
public? true
allow_nil? false
default false
end
attribute :api_key, :string do
private? true
sensitive? true
end
attribute :date_of_birth, :date do
public? true
sensitive? true
end
attribute :profile, Demo.Accounts.Profile
attribute :alternate_profiles, {:array, Demo.Accounts.Profile}
attribute :profile, Demo.Accounts.Profile do
public? true
end
attribute :alternate_profiles, {:array, Demo.Accounts.Profile} do
public? true
end
attribute :type, :atom do
public? true
constraints one_of: [:type1, :type2]
default :type1
end
attribute :types, {:array, :atom} do
public? true
constraints items: [one_of: [:type1, :type2]]
end
attribute :tags, {:array, :string}
attribute :tags, {:array, :string} do
public? true
end
timestamps()
end

View file

@ -1,13 +0,0 @@
defmodule Demo.Tickets.Api do
@moduledoc false
use Ash.Api,
extensions: [AshAdmin.Api]
admin do
show? true
end
resources do
registry Demo.Tickets.Registry
end
end

View file

@ -1,5 +1,6 @@
defmodule Demo.Tickets.Comment do
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: AshPostgres.DataLayer,
extensions: [AshAdmin.Resource]
@ -10,6 +11,7 @@ defmodule Demo.Tickets.Comment do
end
actions do
default_accept :*
defaults [:read, :update, :destroy]
create :create do
primary? true
@ -29,12 +31,12 @@ defmodule Demo.Tickets.Comment do
attributes do
uuid_primary_key :id
attribute :comment, :string
attribute :resource_id, :uuid, allow_nil?: false
attribute :comment, :string, public?: true
attribute :resource_id, :uuid, allow_nil?: false, public?: true
end
relationships do
belongs_to :commenting_customer, Demo.Tickets.Customer
belongs_to :commenting_representative, Demo.Tickets.Representative
belongs_to :commenting_customer, Demo.Tickets.Customer, public?: true
belongs_to :commenting_representative, Demo.Tickets.Representative, public?: true
end
end

View file

@ -0,0 +1,18 @@
defmodule Demo.Tickets.Domain do
@moduledoc false
use Ash.Domain,
extensions: [AshAdmin.Domain]
admin do
show? true
end
resources do
resource(Demo.Tickets.Customer)
resource(Demo.Tickets.Representative)
resource(Demo.Tickets.Ticket)
resource(Demo.Tickets.Comment)
resource(Demo.Tickets.TicketLink)
resource(Demo.Tickets.Organization)
end
end

View file

@ -1,13 +0,0 @@
defmodule Demo.Tickets.Registry do
use Ash.Registry
alias Demo.Tickets.{Comment, Customer, Representative, Ticket, TicketLink, Organization}
entries do
entry(Customer)
entry(Representative)
entry(Ticket)
entry(Comment)
entry(TicketLink)
entry(Organization)
end
end

View file

@ -1,5 +1,6 @@
defmodule Demo.Tickets.Customer do
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: AshPostgres.DataLayer,
authorizers: [
Ash.Policy.Authorizer
@ -33,15 +34,16 @@ defmodule Demo.Tickets.Customer do
end
actions do
default_accept :*
defaults [:read]
end
attributes do
uuid_primary_key :id
attribute :first_name, :string
attribute :last_name, :string
attribute :representative, :boolean
attribute :first_name, :string, public?: true
attribute :last_name, :string, public?: true
attribute :representative, :boolean, public?: true
end
relationships do

View file

@ -1,5 +1,6 @@
defmodule Demo.Tickets.Organization do
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: AshPostgres.DataLayer
postgres do
@ -8,16 +9,17 @@ defmodule Demo.Tickets.Organization do
end
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
end
attributes do
uuid_primary_key :id
attribute :name, :string
attribute :name, :string, public?: true
end
relationships do
has_many :tickets, Demo.Tickets.Ticket
has_many :tickets, Demo.Tickets.Ticket, public?: true
end
end

View file

@ -1,5 +1,6 @@
defmodule Demo.Tickets.Representative do
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: AshPostgres.DataLayer,
authorizers: [
Ash.Policy.Authorizer
@ -38,6 +39,7 @@ defmodule Demo.Tickets.Representative do
end
actions do
default_accept :*
defaults []
read :read do
primary? true
@ -56,9 +58,9 @@ defmodule Demo.Tickets.Representative do
attributes do
uuid_primary_key :id
attribute :first_name, :string
attribute :last_name, :string
attribute :representative, :boolean
attribute :first_name, :string, public?: true
attribute :last_name, :string, public?: true
attribute :representative, :boolean, public?: true
end
aggregates do
@ -71,10 +73,12 @@ defmodule Demo.Tickets.Representative do
relationships do
has_many :assigned_tickets, Demo.Tickets.Ticket do
public? true
destination_attribute :representative_id
end
has_many :comments, Demo.Tickets.Comment do
public? true
relationship_context %{data_layer: %{table: "representative_comments"}}
destination_attribute :resource_id
end

View file

@ -1,5 +1,6 @@
defmodule Demo.Tickets.Ticket do
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: AshPostgres.DataLayer,
authorizers: [
Ash.Policy.Authorizer
@ -38,6 +39,7 @@ defmodule Demo.Tickets.Ticket do
end
actions do
default_accept :*
read :reported do
filter reporter: actor(:id)
@ -82,6 +84,7 @@ defmodule Demo.Tickets.Ticket do
update :assign do
accept []
require_atomic? false
argument :representative, :map
argument :reassignment_comment, :map, allow_nil?: false
@ -91,6 +94,7 @@ defmodule Demo.Tickets.Ticket do
update :link do
accept []
require_atomic? false
argument :tickets, {:array, :map}, allow_nil?: false
argument :link_comment, :map
@ -102,6 +106,7 @@ defmodule Demo.Tickets.Ticket do
update :nested_example do
accept [:subject]
argument :tickets, {:array, :map}
require_atomic? false
change manage_relationship(
:tickets,
@ -127,14 +132,20 @@ defmodule Demo.Tickets.Ticket do
attribute :subject, :string do
allow_nil? false
public? true
constraints min_length: 5
end
attribute :description, :string
attribute :description, :string do
public? true
end
attribute :response, :string
attribute :response, :string do
public? true
end
attribute :status, :atom do
public? true
allow_nil? false
default "new"
constraints one_of: [:new, :investigating, :closed]
@ -148,25 +159,29 @@ defmodule Demo.Tickets.Ticket do
end
relationships do
belongs_to :reporter, Demo.Tickets.Customer
belongs_to :reporter, Demo.Tickets.Customer, public?: true
belongs_to :representative, Demo.Tickets.Representative
belongs_to :representative, Demo.Tickets.Representative, public?: true
belongs_to :organization, Demo.Tickets.Organization do
public? true
allow_nil? false
end
has_many :comments, Demo.Tickets.Comment do
public? true
relationship_context %{data_layer: %{table: "ticket_comments"}}
destination_attribute :resource_id
end
many_to_many :source_links, Demo.Tickets.Ticket do
public? true
through Demo.Tickets.TicketLink
source_attribute_on_join_resource :source_id
destination_attribute_on_join_resource :destination_id
end
many_to_many :destination_links, Demo.Tickets.Ticket do
public? true
through Demo.Tickets.TicketLink
source_attribute_on_join_resource :destination_id
destination_attribute_on_join_resource :source_id

View file

@ -1,5 +1,6 @@
defmodule Demo.Tickets.TicketLink do
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: AshPostgres.DataLayer
postgres do
@ -10,20 +11,23 @@ defmodule Demo.Tickets.TicketLink do
attributes do
attribute :type, :atom, constraints: [
one_of: [:causes, :caused_by, :fixes, :fixed_by]
], allow_nil?: false
], allow_nil?: false, public?: true
end
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
end
relationships do
belongs_to :source, Demo.Tickets.Ticket do
public? true
primary_key? true
allow_nil? false
end
belongs_to :destination, Demo.Tickets.Ticket do
public? true
primary_key? true
allow_nil? false
end

View file

@ -1,7 +1,7 @@
<!--
This file was generated by Spark. Do not edit it by hand.
-->
# DSL: AshAdmin.Api
# DSL: AshAdmin.Domain
An API extension to alter the behavior of an API in the admin UI.

View file

@ -7,7 +7,7 @@ defmodule AshAdmin.ActorPlug.Plug do
@impl true
def actor_assigns(socket, session) do
otp_app = socket.endpoint.config(:otp_app)
apis = apis(otp_app)
domains = domains(otp_app)
session =
Phoenix.LiveView.get_connect_params(socket) || session
@ -21,8 +21,8 @@ defmodule AshAdmin.ActorPlug.Plug do
[
actor: actor_from_session(socket.endpoint, session),
actor_api: actor_api_from_session(socket.endpoint, session),
actor_resources: actor_resources(apis),
actor_domain: actor_domain_from_session(socket.endpoint, session),
actor_resources: actor_resources(domains),
authorizing: session_bool(session["actor_authorizing"]),
tenant: session["tenant"],
actor_paused: actor_paused
@ -50,7 +50,7 @@ defmodule AshAdmin.ActorPlug.Plug do
case session do
%{
"actor_resource" => resource,
"actor_api" => api,
"actor_domain" => domain,
"actor_action" => action,
"actor_primary_key" => primary_key
} ->
@ -70,7 +70,7 @@ defmodule AshAdmin.ActorPlug.Plug do
conn
|> Plug.Conn.put_session(:actor_resource, resource)
|> Plug.Conn.put_session(:actor_api, api)
|> Plug.Conn.put_session(:actor_domain, domain)
|> Plug.Conn.put_session(:actor_action, action)
|> Plug.Conn.put_session(:actor_primary_key, primary_key)
|> Plug.Conn.put_session(:actor_authorizing, authorizing)
@ -105,60 +105,60 @@ defmodule AshAdmin.ActorPlug.Plug do
end
end
defp actor_resources(apis) do
apis
|> Enum.flat_map(fn api ->
api
|> Ash.Api.Info.resources()
defp actor_resources(domains) do
domains
|> Enum.flat_map(fn domain ->
domain
|> Ash.Domain.Info.resources()
|> Enum.filter(fn resource ->
AshAdmin.Helpers.primary_action(resource, :read) && AshAdmin.Resource.actor?(resource)
end)
|> Enum.map(fn resource -> {api, resource} end)
|> Enum.map(fn resource -> {domain, resource} end)
end)
end
defp apis(otp_app) do
defp domains(otp_app) do
otp_app
|> Application.get_env(:ash_apis)
|> Enum.filter(&AshAdmin.Api.show?/1)
|> Application.get_env(:ash_domains)
|> Enum.filter(&AshAdmin.Domain.show?/1)
end
defp actor_api_from_session(endpoint, %{"actor_api" => api}) do
defp actor_domain_from_session(endpoint, %{"actor_domain" => domain}) do
otp_app = endpoint.config(:otp_app)
apis = Application.get_env(otp_app, :ash_apis)
domains = Application.get_env(otp_app, :ash_domains)
Enum.find(apis, fn allowed_api ->
AshAdmin.Api.show?(allowed_api) && AshAdmin.Api.name(allowed_api) == api
Enum.find(domains, fn allowed_domain ->
AshAdmin.Domain.show?(allowed_domain) && AshAdmin.Domain.name(allowed_domain) == domain
end)
end
defp actor_api_from_session(_, _), do: nil
defp actor_domain_from_session(_, _), do: nil
defp actor_from_session(endpoint, %{
"actor_resource" => resource,
"actor_api" => api,
"actor_domain" => domain,
"actor_primary_key" => primary_key,
"actor_action" => action
})
when not is_nil(resource) and not is_nil(api) do
when not is_nil(resource) and not is_nil(domain) do
otp_app = endpoint.config(:otp_app)
apis = Application.get_env(otp_app, :ash_apis)
domains = Application.get_env(otp_app, :ash_domains)
api =
Enum.find(apis, fn allowed_api ->
AshAdmin.Api.show?(allowed_api) && AshAdmin.Api.name(allowed_api) == api
domain =
Enum.find(domains, fn allowed_domain ->
AshAdmin.Domain.show?(allowed_domain) && AshAdmin.Domain.name(allowed_domain) == domain
end)
resource =
if api do
api
|> Ash.Api.Info.resources()
|> Enum.find(fn api_resource ->
AshAdmin.Resource.name(api_resource) == resource
if domain do
domain
|> Ash.Domain.Info.resources()
|> Enum.find(fn domain_resource ->
AshAdmin.Resource.name(domain_resource) == resource
end)
end
if api && resource do
if domain && resource do
action =
if action do
Ash.Resource.Info.action(resource, String.to_existing_atom(action), :read)
@ -171,7 +171,7 @@ defmodule AshAdmin.ActorPlug.Plug do
{:ok, filter} ->
resource
|> Ash.Query.filter(^filter)
|> api.read_one!(action: action, authorize?: false)
|> Ash.read_one!(action: action, authorize?: false, domain: domain)
end
end
end

View file

@ -8,7 +8,7 @@ defmodule AshAdmin.Components.Resource.DataTable do
alias AshAdmin.Components.Resource.Table
attr :resource, :atom
attr :api, :atom
attr :domain, :atom
attr :action, :any
attr :authorizing, :boolean
attr :actor, :any
@ -100,7 +100,7 @@ defmodule AshAdmin.Components.Resource.DataTable do
table={@table}
data={data(@data)}
resource={@resource}
api={@api}
domain={@domain}
attributes={AshAdmin.Resource.table_columns(@resource)}
format_fields={AshAdmin.Resource.format_fields(@resource)}
show_sensitive_fields={AshAdmin.Resource.show_sensitive_fields(@resource)}
@ -212,7 +212,7 @@ defmodule AshAdmin.Components.Resource.DataTable do
socket.assigns.query.source
|> set_table(socket.assigns[:table])
|> load_fields()
|> assigns[:api].read(page: page_params)
|> Ash.read(page: page_params, domain: assigns[:domain])
end
end,
load_until_connected?: true
@ -230,7 +230,7 @@ defmodule AshAdmin.Components.Resource.DataTable do
|> set_table(socket.assigns[:table])
|> Ash.Query.limit(1000)
|> load_fields()
|> assigns[:api].read()
|> Ash.read(domain: assigns[:domain])
end
end,
load_until_connected?: true

View file

@ -9,7 +9,7 @@ defmodule AshAdmin.Components.Resource.Form do
require Logger
attr :resource, :any, required: true
attr :api, :any, required: true
attr :domain, :any, required: true
attr :record, :any, default: nil
attr :type, :atom, default: nil
attr :actor, :any, default: nil
@ -1043,10 +1043,10 @@ defmodule AshAdmin.Components.Resource.Form do
type
|> Ash.Resource.Info.primary_key()
|> Enum.flat_map(fn attr ->
if Ash.Resource.Info.attribute(type, attr).private? do
[]
else
if Ash.Resource.Info.attribute(type, attr).public? do
[attr]
else
[]
end
end)
|> Enum.join("-")
@ -1109,7 +1109,7 @@ defmodule AshAdmin.Components.Resource.Form do
socket
|> redirect(
to:
"#{socket.assigns.prefix || "/"}?api=#{AshAdmin.Api.name(socket.assigns.api)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}&tab=show&table=#{socket.assigns.table}&primary_key=#{encode_primary_key(record)}"
"#{socket.assigns.prefix || "/"}?domain=#{AshAdmin.Domain.name(socket.assigns.domain)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}&tab=show&table=#{socket.assigns.table}&primary_key=#{encode_primary_key(record)}"
)}
else
case AshAdmin.Helpers.primary_action(socket.assigns.resource, :update) do
@ -1117,7 +1117,7 @@ defmodule AshAdmin.Components.Resource.Form do
{:noreply,
redirect(socket,
to:
"#{socket.assigns.prefix || "/"}?api=#{AshAdmin.Api.name(socket.assigns.api)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}"
"#{socket.assigns.prefix || "/"}?domain=#{AshAdmin.Domain.name(socket.assigns.domain)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}"
)}
_update ->
@ -1125,7 +1125,7 @@ defmodule AshAdmin.Components.Resource.Form do
socket
|> redirect(
to:
"#{socket.assigns.prefix || "/"}?api=#{AshAdmin.Api.name(socket.assigns.api)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}&action_type=update&tab=update&table=#{socket.assigns.table}&primary_key=#{encode_primary_key(record)}"
"#{socket.assigns.prefix || "/"}?domain=#{AshAdmin.Domain.name(socket.assigns.domain)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}&action_type=update&tab=update&table=#{socket.assigns.table}&primary_key=#{encode_primary_key(record)}"
)}
end
end
@ -1230,7 +1230,7 @@ defmodule AshAdmin.Components.Resource.Form do
path,
fn adding_form ->
if adding_form.data do
new_data = socket.assigns.api.load!(adding_form.data, relationship)
new_data = Ash.load!(adding_form.data, relationship, domain: socket.assigns.domain)
if Map.has_key?(adding_form.source, :data) do
%{adding_form | data: new_data, source: %{adding_form.source | data: new_data}}
@ -1285,7 +1285,7 @@ defmodule AshAdmin.Components.Resource.Form do
socket
|> redirect(
to:
"#{socket.assigns.prefix}?api=#{AshAdmin.Api.name(socket.assigns.api)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}"
"#{socket.assigns.prefix}?domain=#{AshAdmin.Domain.name(socket.assigns.domain)}&resource=#{AshAdmin.Resource.name(socket.assigns.resource)}"
)}
{:error, form} ->
@ -1404,8 +1404,7 @@ defmodule AshAdmin.Components.Resource.Form do
def relationships(resource, action, nil) do
resource
|> Ash.Resource.Info.relationships()
|> Enum.filter(& &1.writable?)
|> Enum.reject(& &1.private?)
|> Enum.filter(& &1.writable? && &1.public?)
|> only_accepted(action)
|> sort_relationships()
end
@ -1454,20 +1453,20 @@ defmodule AshAdmin.Components.Resource.Form do
def attributes(resource, %{type: :destroy} = action, _) do
action.arguments
|> Enum.reject(& &1.private?)
|> Enum.filter(& &1.public?)
|> sort_attributes(resource, action)
end
def attributes(resource, action, _) do
arguments =
action.arguments
|> Enum.reject(& &1.private?)
|> Enum.filter(& &1.public?)
attributes =
resource
|> Ash.Resource.Info.attributes()
|> Enum.filter(& &1.writable?)
|> Enum.reject(&(&1.private? || &1.name == :autogenerated_id))
|> Enum.filter(& &1.writable? && &1.public?)
|> Enum.reject(&(&1.name == :autogenerated_id))
|> only_accepted(action)
|> Enum.reject(fn attribute ->
Enum.any?(arguments, &(&1.name == attribute.name))
@ -1623,7 +1622,7 @@ defmodule AshAdmin.Components.Resource.Form do
:create ->
socket.assigns.resource
|> AshPhoenix.Form.for_create(socket.assigns.action.name,
api: socket.assigns.api,
domain: socket.assigns.domain,
actor: socket.assigns[:actor],
authorize?: socket.assigns[:authorizing],
forms: auto_forms,
@ -1634,7 +1633,7 @@ defmodule AshAdmin.Components.Resource.Form do
:update ->
socket.assigns.record
|> AshPhoenix.Form.for_update(socket.assigns.action.name,
api: socket.assigns.api,
domain: socket.assigns.domain,
forms: auto_forms,
actor: socket.assigns[:actor],
authorize?: socket.assigns[:authorizing],
@ -1645,7 +1644,7 @@ defmodule AshAdmin.Components.Resource.Form do
:destroy ->
socket.assigns.record
|> AshPhoenix.Form.for_destroy(socket.assigns.action.name,
api: socket.assigns.api,
domain: socket.assigns.domain,
forms: auto_forms,
actor: socket.assigns[:actor],
authorize?: socket.assigns[:authorizing],

View file

@ -5,14 +5,14 @@ defmodule AshAdmin.Components.Resource.Info do
alias AshAdmin.Components.Resource.MetadataTable
attr :resource, :any, required: true
attr :api, :any, required: true
attr :domain, :any, required: true
attr :prefix, :any, required: true
def info(assigns) do
~H"""
<div class="relative mx-12">
<MetadataTable.attribute_table resource={@resource} />
<MetadataTable.relationship_table api={@api} resource={@resource} prefix={@prefix} />
<MetadataTable.relationship_table domain={@domain} resource={@resource} prefix={@prefix} />
</div>
"""
end

View file

@ -45,7 +45,7 @@ defmodule AshAdmin.Components.Resource.MetadataTable do
</.td>
<.td>
<CoreComponents.icon
name={if attribute.private?, do: "hero-check", else: "hero-x-mark"}
name={if !attribute.public?, do: "hero-check", else: "hero-x-mark"}
class="h-4 w-4 text-gray-500"
/>
</.td>
@ -69,7 +69,7 @@ defmodule AshAdmin.Components.Resource.MetadataTable do
end
attr :resource, :any, required: true
attr :api, :any, required: true
attr :domain, :any, required: true
attr :prefix, :any, required: true
def relationship_table(assigns) do
@ -98,7 +98,7 @@ defmodule AshAdmin.Components.Resource.MetadataTable do
<%= relationship.type %>
</.td>
<.td>
<.link navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(relationship.destination)}"}>
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(relationship.destination)}"}>
<%= AshAdmin.Resource.name(relationship.destination) %>
</.link>
</.td>
@ -157,7 +157,7 @@ defmodule AshAdmin.Components.Resource.MetadataTable do
defp attributes(resource) do
resource
|> Ash.Resource.Info.attributes()
|> Enum.sort_by(& &1.private?)
|> Enum.sort_by(& not(&1.public?))
end
defp attribute_type(attribute) do
@ -173,6 +173,6 @@ defmodule AshAdmin.Components.Resource.MetadataTable do
defp relationships(resource) do
resource
|> Ash.Resource.Info.relationships()
|> Enum.sort_by(& &1.private?)
|> Enum.sort_by(& not(&1.public?))
end
end

View file

@ -4,7 +4,7 @@ defmodule AshAdmin.Components.Resource.Nav do
alias AshAdmin.Components.TopNav.Dropdown
attr :resource, :any, required: true
attr :api, :any, required: true
attr :domain, :any, required: true
attr :tab, :string, required: true
attr :action, :any
attr :table, :any, default: nil
@ -18,7 +18,7 @@ defmodule AshAdmin.Components.Resource.Nav do
<div class="flex items-center w-full">
<div class="flex-shrink-0">
<h3 class="text-white text-lg">
<.link navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}"}>
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}"}>
<%= AshAdmin.Resource.name(@resource) %>
</.link>
</h3>
@ -27,7 +27,7 @@ defmodule AshAdmin.Components.Resource.Nav do
<div class="ml-12 flex items-center space-x-1">
<div :if={has_create_action?(@resource)} class="relative">
<.link
navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=create&action=#{create_action(@resource).name}&tab=create&table=#{@table}"}
navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=create&action=#{create_action(@resource).name}&tab=create&table=#{@table}"}
class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500"
>
Create
@ -39,7 +39,7 @@ defmodule AshAdmin.Components.Resource.Nav do
name="Read"
id={"#{@resource}_data_dropdown"}
active={@tab == "data"}
groups={data_groups(@prefix, @api, @resource, @action, @table)}
groups={data_groups(@prefix, @domain, @resource, @action, @table)}
/>
</div>
</div>
@ -55,7 +55,7 @@ defmodule AshAdmin.Components.Resource.Nav do
Enum.find(Ash.Resource.Info.actions(resource), &(&1.type == :create))
end
defp data_groups(prefix, api, resource, current_action, table) do
defp data_groups(prefix, domain, resource, current_action, table) do
read_actions = AshAdmin.Resource.read_actions(resource)
[
@ -66,7 +66,7 @@ defmodule AshAdmin.Components.Resource.Nav do
%{
text: action_name(action),
to:
"#{prefix}?api=#{AshAdmin.Api.name(api)}&resource=#{AshAdmin.Resource.name(resource)}&table=#{table}&action_type=read&action=#{action.name}",
"#{prefix}?domain=#{AshAdmin.Domain.name(domain)}&resource=#{AshAdmin.Resource.name(resource)}&table=#{table}&action_type=read&action=#{action.name}",
active: current_action == action
}
end)

View file

@ -8,7 +8,7 @@ defmodule AshAdmin.Components.Resource do
# prop hide_filter, :boolean, default: true
attr :resource, :any, required: true
attr :api, :any, required: true
attr :domain, :any, required: true
attr :tab, :string, required: true
attr :action, :any
attr :actor, :any, required: true
@ -29,7 +29,7 @@ defmodule AshAdmin.Components.Resource do
<div class="h-screen">
<Nav.nav
resource={@resource}
api={@api}
domain={@domain}
tab={@tab}
action={@action}
table={@table}
@ -50,7 +50,7 @@ defmodule AshAdmin.Components.Resource do
url_path={@url_path}
params={@params}
action={@action}
api={@api}
domain={@domain}
id={update_id(@resource)}
actor={@actor}
authorizing={@authorizing}
@ -73,7 +73,7 @@ defmodule AshAdmin.Components.Resource do
url_path={@url_path}
action={@action}
params={@params}
api={@api}
domain={@domain}
id={destroy_id(@resource)}
actor={@actor}
authorizing={@authorizing}
@ -87,7 +87,7 @@ defmodule AshAdmin.Components.Resource do
:if={@tab == "show" && match?({:ok, %_{}}, @record)}
module={Show}
resource={@resource}
api={@api}
domain={@domain}
id={show_id(@resource)}
record={unwrap(@record)}
actor={@actor}
@ -99,7 +99,7 @@ defmodule AshAdmin.Components.Resource do
<Info.info
:if={@tab == "info" || (is_nil(@tab) && is_nil(@action_type))}
resource={@resource}
api={@api}
domain={@domain}
prefix={@prefix}
/>
<.live_component
@ -109,7 +109,7 @@ defmodule AshAdmin.Components.Resource do
resource={@resource}
url_path={@url_path}
params={@params}
api={@api}
domain={@domain}
action={@action}
id={create_id(@resource)}
actor={@actor}
@ -127,7 +127,7 @@ defmodule AshAdmin.Components.Resource do
resource={@resource}
action={@action}
actor={@actor}
api={@api}
domain={@domain}
url_path={@url_path}
params={@params}
id={data_table_id(@resource)}

View file

@ -9,7 +9,7 @@ defmodule AshAdmin.Components.Resource.Show do
attr :resource, :any
attr :record, :any, default: nil
attr :api, :any, default: nil
attr :domain, :any, default: nil
attr :action, :any
attr :authorizing, :boolean, default: false
attr :actor, :any
@ -45,7 +45,7 @@ defmodule AshAdmin.Components.Resource.Show do
class="float-right pt-4 pr-4"
phx-click="set_actor"
phx-value-resource={@resource}
phx-value-api={@api}
phx-value-domain={@domain}
phx-value-pkey={encode_primary_key(@record)}
>
<.icon name="hero-key" class="h-5 w-5 text-gray-500" />
@ -56,7 +56,7 @@ defmodule AshAdmin.Components.Resource.Show do
<div :if={@buttons} class="px-4 py-3 text-right sm:px-6">
<.link
:if={destroy?(@resource)}
navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=destroy&action=#{primary_action_name(@resource, :destroy)}&tab=destroy&table=#{@table}&primary_key=#{encode_primary_key(@record)}"}
navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=destroy&action=#{primary_action_name(@resource, :destroy)}&tab=destroy&table=#{@table}&primary_key=#{encode_primary_key(@record)}"}
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Destroy
@ -64,7 +64,7 @@ defmodule AshAdmin.Components.Resource.Show do
<.link
:if={update?(@resource)}
navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=update&action=#{primary_action_name(@resource, :update)}&tab=update&table=#{@table}&primary_key=#{encode_primary_key(@record)}"}
navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=update&action=#{primary_action_name(@resource, :update)}&tab=update&table=#{@table}&primary_key=#{encode_primary_key(@record)}"}
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Update
@ -131,7 +131,7 @@ defmodule AshAdmin.Components.Resource.Show do
name: name,
destination: destination,
context: context,
api: destination_api
domain: destination_domain
}) do
case Map.get(record, name) do
nil ->
@ -144,7 +144,7 @@ defmodule AshAdmin.Components.Resource.Show do
name: name,
destination: destination,
context: context,
destination_api: destination_api
destination_domain: destination_domain
)
~H"""
@ -153,7 +153,7 @@ defmodule AshAdmin.Components.Resource.Show do
<div class="px-4 py-3 text-right sm:px-6">
<.link
:if={AshAdmin.Resource.show_action(@destination)}
navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@destination_api || @api)}&resource=#{AshAdmin.Resource.name(@destination)}&tab=show&table=#{@context[:data_layer][:table]}&primary_key=#{encode_primary_key(@record)}"}
navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@destination_domain || @domain)}&resource=#{AshAdmin.Resource.name(@destination)}&tab=show&table=#{@context[:data_layer][:table]}&primary_key=#{encode_primary_key(@record)}"}
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Show
@ -186,7 +186,7 @@ defmodule AshAdmin.Components.Resource.Show do
<Table.table
data={@data}
resource={@destination}
api={@api}
domain={@domain}
table={@context[:data_layer][:table]}
prefix={@prefix}
skip={[@destination_attribute]}
@ -470,12 +470,13 @@ defmodule AshAdmin.Components.Resource.Show do
def handle_event("load", %{"relationship" => relationship}, socket) do
record = socket.assigns.record
api = socket.assigns.api
domain = socket.assigns.domain
relationship = String.to_existing_atom(relationship)
case api.load(
case Ash.load(
record,
relationship,
domain: domain,
actor: socket.assigns[:actor],
authorize?: socket.assigns[:authorizing]
) do

View file

@ -11,7 +11,7 @@ defmodule AshAdmin.Components.Resource.Table do
attr :data, :list, default: nil
attr :resource, :any, required: true
attr :actions, :boolean, default: true
attr :api, :any, required: true
attr :domain, :any, required: true
attr :table, :any, required: true
attr :prefix, :any, required: true
attr :skip, :list, default: []
@ -32,7 +32,7 @@ defmodule AshAdmin.Components.Resource.Table do
<tr :for={record <- @data} class="border-b-2">
<td :for={attribute <- attributes(@resource, @attributes, @skip)} class="py-3">
<%= render_attribute(
@api,
@domain,
record,
attribute,
@format_fields,
@ -43,19 +43,19 @@ defmodule AshAdmin.Components.Resource.Table do
<td :if={@actions && actions?(@resource)}>
<div class="flex h-max justify-items-center">
<div :if={AshAdmin.Resource.show_action(@resource)}>
<.link navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&tab=show&table=#{@table}&primary_key=#{encode_primary_key(record)}"}>
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&tab=show&table=#{@table}&primary_key=#{encode_primary_key(record)}"}>
<.icon name="hero-information-circle-solid" class="h-5 w-5 text-gray-500" />
</.link>
</div>
<div :if={AshAdmin.Helpers.primary_action(@resource, :update)}>
<.link navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=update&action=#{AshAdmin.Helpers.primary_action(@resource, :update).name}&tab=update&table=#{@table}&primary_key=#{encode_primary_key(record)}"}>
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=update&action=#{AshAdmin.Helpers.primary_action(@resource, :update).name}&tab=update&table=#{@table}&primary_key=#{encode_primary_key(record)}"}>
<.icon name="hero-pencil-solid" class="h-5 w-5 text-gray-500" />
</.link>
</div>
<div :if={AshAdmin.Helpers.primary_action(@resource, :destroy)}>
<.link navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=destroy&action=#{AshAdmin.Helpers.primary_action(@resource, :destroy).name}&tab=destroy&table=#{@table}&primary_key=#{encode_primary_key(record)}"}>
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=destroy&action=#{AshAdmin.Helpers.primary_action(@resource, :destroy).name}&tab=destroy&table=#{@table}&primary_key=#{encode_primary_key(record)}"}>
<.icon name="hero-x-circle-solid" class="h-5 w-5 text-gray-500" />
</.link>
</div>
@ -64,7 +64,7 @@ defmodule AshAdmin.Components.Resource.Table do
:if={AshAdmin.Resource.actor?(@resource)}
phx-click="set_actor"
phx-value-resource={@resource}
phx-value-api={@api}
phx-value-domain={@domain}
phx-value-pkey={encode_primary_key(record)}
>
<.icon name="hero-key-solid" class="h-5 w-5 text-gray-500" />
@ -99,15 +99,15 @@ defmodule AshAdmin.Components.Resource.Table do
|> Enum.reject(&(&1.name in skip))
end
defp render_attribute(api, record, attribute, formats, show_sensitive_fields, actor) do
process_attribute(api, record, attribute, formats, show_sensitive_fields, actor)
defp render_attribute(domain, record, attribute, formats, show_sensitive_fields, actor) do
process_attribute(domain, record, attribute, formats, show_sensitive_fields, actor)
rescue
_ ->
"..."
end
defp process_attribute(
api,
domain,
record,
%module{} = attribute,
formats,
@ -124,7 +124,7 @@ defmodule AshAdmin.Components.Resource.Table do
if loaded?(record, attribute.name) do
record
else
api.load!(record, [{attribute.name, display_attributes}], actor: actor)
Ash.load!(record, [{attribute.name, display_attributes}], actor: actor, domain: domain)
end
relationship = Map.get(record, attribute.name)
@ -135,7 +135,7 @@ defmodule AshAdmin.Components.Resource.Table do
attributes = attributes(attribute.destination, display_attributes, [])
Enum.map_join(attributes, " - ", fn x ->
render_attribute(api, relationship, x, formats, show_sensitive_fields, actor)
render_attribute(domain, relationship, x, formats, show_sensitive_fields, actor)
end)
end
end
@ -159,7 +159,7 @@ defmodule AshAdmin.Components.Resource.Table do
end
end
defp process_attribute(_api, _record, _attr, _formats, _show_sensitive_fields, _actor) do
defp process_attribute(_domain, _record, _attr, _formats, _show_sensitive_fields, _actor) do
"..."
end

View file

@ -4,10 +4,10 @@ defmodule AshAdmin.Components.TopNav do
import Tails
alias AshAdmin.Components.TopNav.{ActorSelect, DrawerDropdown, Dropdown, TenantForm}
attr :api, :any, required: true
attr :domain, :any, required: true
attr :resource, :any, required: true
attr :actor_resources, :any, required: true
attr :apis, :any, required: true
attr :domains, :any, required: true
attr :tenant, :any, required: true
attr :clear_tenant, :string
attr :set_tenant, :string
@ -17,7 +17,7 @@ defmodule AshAdmin.Components.TopNav do
attr :authorizing, :boolean, required: true
attr :actor_paused, :boolean, required: true
attr :actor, :any, required: true
attr :actor_api, :any, required: true
attr :actor_domain, :any, required: true
attr :prefix, :any, required: true
def render(assigns) do
@ -37,14 +37,14 @@ defmodule AshAdmin.Components.TopNav do
<div class="flex justify-between">
<div class="ml-10 flex items-center">
<.live_component
:for={api <- @apis}
:for={domain <- @domains}
module={Dropdown}
active={api == @api}
active={domain == @domain}
class="mr-1"
id={AshAdmin.Api.name(api) <> "_api_nav"}
name={AshAdmin.Api.name(api)}
groups={dropdown_groups(@prefix, @resource, api)}
group_labels={dropdown_group_labels(api)}
id={AshAdmin.Domain.name(domain) <> "_domain_nav"}
name={AshAdmin.Domain.name(domain)}
groups={dropdown_groups(@prefix, @resource, domain)}
group_labels={dropdown_group_labels(domain)}
/>
</div>
<div class="ml-10 flex items-center">
@ -57,12 +57,12 @@ defmodule AshAdmin.Components.TopNav do
toggle_authorizing={@toggle_authorizing}
toggle_actor_paused={@toggle_actor_paused}
clear_actor={@clear_actor}
actor_api={@actor_api}
api={@api}
actor_domain={@actor_domain}
domain={@domain}
prefix={@prefix}
/>
<TenantForm.tenant_form
:if={show_tenant_form?(@apis)}
:if={show_tenant_form?(@domains)}
tenant={@tenant}
editing_tenant={@editing_tenant}
set_tenant={@set_tenant}
@ -111,14 +111,14 @@ defmodule AshAdmin.Components.TopNav do
toggle_authorizing={@toggle_authorizing}
toggle_actor_paused={@toggle_actor_paused}
clear_actor={@clear_actor}
actor_api={@actor_api}
api={@api}
actor_domain={@actor_domain}
domain={@domain}
prefix={@prefix}
/>
</div>
<div class="block px-4 py-2 text-sm">
<.live_component
:if={show_tenant_form?(@apis)}
:if={show_tenant_form?(@domains)}
module={TenantForm}
tenant={@tenant}
id="tenant_editor_drawer"
@ -127,12 +127,12 @@ defmodule AshAdmin.Components.TopNav do
/>
</div>
<.live_component
:for={api <- @apis}
:for={domain <- @domains}
module={DrawerDropdown}
id={AshAdmin.Api.name(api) <> "_api_nav_drawer"}
name={AshAdmin.Api.name(api)}
groups={dropdown_groups(@prefix, @resource, api)}
group_labels={dropdown_group_labels(api)}
id={AshAdmin.Domain.name(domain) <> "_domain_nav_drawer"}
name={AshAdmin.Domain.name(domain)}
groups={dropdown_groups(@prefix, @resource, domain)}
group_labels={dropdown_group_labels(domain)}
/>
</div>
</div>
@ -140,12 +140,12 @@ defmodule AshAdmin.Components.TopNav do
"""
end
defp dropdown_groups(prefix, current_resource, api) do
for resource <- Ash.Api.Info.resources(api) do
defp dropdown_groups(prefix, current_resource, domain) do
for resource <- Ash.Domain.Info.resources(domain) do
%{
text: AshAdmin.Resource.name(resource),
to:
"#{prefix}?api=#{AshAdmin.Api.name(api)}&resource=#{AshAdmin.Resource.name(resource)}",
"#{prefix}?domain=#{AshAdmin.Domain.name(domain)}&resource=#{AshAdmin.Resource.name(resource)}",
active: resource == current_resource,
group: AshAdmin.Resource.resource_group(resource)
}
@ -155,8 +155,8 @@ defmodule AshAdmin.Components.TopNav do
|> Keyword.values()
end
defp dropdown_group_labels(api) do
AshAdmin.Api.resource_group_labels(api)
defp dropdown_group_labels(domain) do
AshAdmin.Domain.resource_group_labels(domain)
end
def mount(socket) do
@ -175,10 +175,10 @@ defmodule AshAdmin.Components.TopNav do
{:noreply, assign(socket, :open, !socket.assigns.open)}
end
defp show_tenant_form?(apis) do
Enum.any?(apis, fn api ->
api
|> Ash.Api.Info.resources()
defp show_tenant_form?(domains) do
Enum.any?(domains, fn domain ->
domain
|> Ash.Domain.Info.resources()
|> Enum.any?(fn resource ->
Ash.Resource.Info.multitenancy_strategy(resource)
end)

View file

@ -11,8 +11,8 @@ defmodule AshAdmin.Components.TopNav.ActorSelect do
attr :toggle_authorizing, :string, required: true
attr :toggle_actor_paused, :string, required: true
attr :clear_actor, :string, required: true
attr :api, :any, required: true
attr :actor_api, :any, required: true
attr :domain, :any, required: true
attr :actor_domain, :any, required: true
attr :prefix, :any, required: true
def actor_select(assigns) do
@ -73,7 +73,7 @@ defmodule AshAdmin.Components.TopNav.ActorSelect do
<.link
:if={@actor}
class="hover:text-blue-400 hover:underline"
target={"#{@prefix}?api=#{AshAdmin.Api.name(@actor_api)}&resource=#{AshAdmin.Resource.name(@actor.__struct__)}&tab=show&primary_key=#{encode_primary_key(@actor)}"}
target={"#{@prefix}?domain=#{AshAdmin.Domain.name(@actor_domain)}&resource=#{AshAdmin.Resource.name(@actor.__struct__)}&tab=show&primary_key=#{encode_primary_key(@actor)}"}
>
<%= user_display(@actor) %>
</.link>
@ -104,27 +104,27 @@ defmodule AshAdmin.Components.TopNav.ActorSelect do
"""
end
defp render_actor_link(assigns, [{api, resource}]) do
assigns = assign(assigns, api: api, resource: resource)
defp render_actor_link(assigns, [{domain, resource}]) do
assigns = assign(assigns, domain: domain, resource: resource)
~H"""
<.link navigate={"#{@prefix}?api=#{AshAdmin.Api.name(@api)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=read"}>
<.link navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(@domain)}&resource=#{AshAdmin.Resource.name(@resource)}&action_type=read"}>
Set <%= AshAdmin.Resource.name(@resource) %>
</.link>
"""
end
defp render_actor_link(assigns, apis_and_resources) do
assigns = assign(assigns, apis_and_resources: apis_and_resources)
defp render_actor_link(assigns, domains_and_resources) do
assigns = assign(assigns, domains_and_resources: domains_and_resources)
~H"""
<div aria-labelledby="actor-banner">
<.link
:for={{{api, resource}, i} <- Enum.with_index(@apis_and_resources)}
navigate={"#{@prefix}?api=#{AshAdmin.Api.name(api)}&resource=#{AshAdmin.Resource.name(resource)}&action_type=read"}
:for={{{domain, resource}, i} <- Enum.with_index(@domains_and_resources)}
navigate={"#{@prefix}?domain=#{AshAdmin.Domain.name(domain)}&resource=#{AshAdmin.Resource.name(resource)}&action_type=read"}
>
Set <%= AshAdmin.Resource.name(resource) %>
<span :if={i != Enum.count(@apis_and_resources) - 1}>
<span :if={i != Enum.count(@domains_and_resources) - 1}>
|
</span>
</.link>

View file

@ -1,17 +1,17 @@
defmodule AshAdmin.Api do
defmodule AshAdmin.Domain do
@admin %Spark.Dsl.Section{
describe: "Configure the admin dashboard for a given API.",
describe: "Configure the admin dashboard for a given domain.",
name: :admin,
schema: [
name: [
type: :string,
doc: "The name of the API in the dashboard. Will be derived if not set."
doc: "The name of the domain in the dashboard. Will be derived if not set."
],
show?: [
type: :boolean,
default: false,
doc:
"Whether or not this API and its resources should be included in the admin dashboard."
"Whether or not this domain and its resources should be included in the admin dashboard."
],
default_resource_page: [
type: {:in, [:schema, :primary_read]},
@ -31,30 +31,30 @@ defmodule AshAdmin.Api do
use Spark.Dsl.Extension, sections: [@admin]
@moduledoc """
An API extension to alter the behavior of an API in the admin UI.
A domain extension to alter the behavior of a domain in the admin UI.
"""
def name(api) do
Spark.Dsl.Extension.get_opt(api, [:admin], :name, nil, true) || default_name(api)
def name(domain) do
Spark.Dsl.Extension.get_opt(domain, [:admin], :name, nil, true) || default_name(domain)
end
def show?(api) do
Spark.Dsl.Extension.get_opt(api, [:admin], :show?, false, true)
def show?(domain) do
Spark.Dsl.Extension.get_opt(domain, [:admin], :show?, false, true)
end
def default_resource_page(api) do
Spark.Dsl.Extension.get_opt(api, [:admin], :default_resource_page, :schema, true)
def default_resource_page(domain) do
Spark.Dsl.Extension.get_opt(domain, [:admin], :default_resource_page, :schema, true)
end
def resource_group_labels(api) do
Spark.Dsl.Extension.get_opt(api, [:admin], :resource_group_labels, [], true)
def resource_group_labels(domain) do
Spark.Dsl.Extension.get_opt(domain, [:admin], :resource_group_labels, [], true)
end
defp default_name(api) do
split = api |> Module.split()
defp default_name(domain) do
split = domain |> Module.split()
case List.last(split) do
"Api" ->
"Domain" ->
Enum.at(split, -2)
last ->

View file

@ -39,20 +39,20 @@ defmodule AshAdmin.PageLive do
socket = assign(socket, :prefix, prefix)
apis = apis(otp_app)
domains = domains(otp_app)
{:ok,
socket
|> assign(:prefix, prefix)
|> assign(:primary_key, nil)
|> assign(:record, nil)
|> assign(:apis, apis)
|> assign(:domains, domains)
|> assign(:tenant, session["tenant"])
|> assign(:editing_tenant, false)
|> then(fn socket ->
assign(socket, AshAdmin.ActorPlug.actor_assigns(socket, session))
end)
|> assign_new(:actor_api, fn -> nil end)
|> assign_new(:actor_domain, fn -> nil end)
|> assign_new(:actor_resources, fn -> [] end)
|> assign_new(:authorizing, fn -> true end)
|> assign_new(:actor_paused, fn -> false end)}
@ -64,10 +64,10 @@ defmodule AshAdmin.PageLive do
<.live_component
module={TopNav}
id="top_nav"
apis={@apis}
api={@api}
domains={@domains}
domain={@domain}
editing_tenant={@editing_tenant}
actor_api={@actor_api}
actor_domain={@actor_domain}
resource={@resource}
tenant={@tenant}
actor_resources={@actor_resources}
@ -89,7 +89,7 @@ defmodule AshAdmin.PageLive do
set_actor="set_actor"
primary_key={@primary_key}
record={@record}
api={@api}
domain={@domain}
tab={@tab}
action_type={@action_type}
url_path={@url_path}
@ -106,28 +106,28 @@ defmodule AshAdmin.PageLive do
"""
end
defp apis(otp_app) do
defp domains(otp_app) do
otp_app
|> Application.get_env(:ash_apis)
|> Enum.filter(&AshAdmin.Api.show?/1)
|> Application.get_env(:ash_domains)
|> Enum.filter(&AshAdmin.Domain.show?/1)
end
defp assign_api(socket, api) do
api =
Enum.find(socket.assigns.apis, fn shown_api ->
AshAdmin.Api.name(shown_api) == api
end) || Enum.at(socket.assigns.apis, 0)
defp assign_domain(socket, domain) do
domain =
Enum.find(socket.assigns.domains, fn shown_domain ->
AshAdmin.Domain.name(shown_domain) == domain
end) || Enum.at(socket.assigns.domains, 0)
assign(socket, :api, api)
assign(socket, :domain, domain)
end
defp assign_resource(socket, resource) do
if socket.assigns.api do
resources = Ash.Api.Info.resources(socket.assigns.api)
if socket.assigns.domain do
resources = Ash.Domain.Info.resources(socket.assigns.domain)
resource =
Enum.find(resources, fn api_resource ->
AshAdmin.Resource.name(api_resource) == resource
Enum.find(resources, fn domain_resources ->
AshAdmin.Resource.name(domain_resources) == resource
end) || Enum.at(resources, 0)
assign(socket, :resource, resource)
@ -139,7 +139,7 @@ defmodule AshAdmin.PageLive do
defp assign_action(socket, action, action_type) do
requested_action = action
if socket.assigns.api && socket.assigns.resource do
if socket.assigns.domain && socket.assigns.resource do
action_type =
case action_type do
"read" ->
@ -155,7 +155,7 @@ defmodule AshAdmin.PageLive do
:destroy
nil ->
if AshAdmin.Api.default_resource_page(socket.assigns.api) == :primary_read,
if AshAdmin.Domain.default_resource_page(socket.assigns.domain) == :primary_read,
do: :read,
else: nil
end
@ -242,7 +242,7 @@ defmodule AshAdmin.PageLive do
if socket.assigns.resource do
tables =
if socket.assigns.resource do
AshAdmin.Resource.polymorphic_tables(socket.assigns.resource, socket.assigns.apis)
AshAdmin.Resource.polymorphic_tables(socket.assigns.resource, socket.assigns.domains)
else
[]
end
@ -271,7 +271,7 @@ defmodule AshAdmin.PageLive do
socket =
socket
|> assign_api(params["api"])
|> assign_domain(params["domain"])
|> assign_resource(params["resource"])
|> assign_action(params["action"], params["action_type"])
|> assign_tables(params["table"])
@ -298,15 +298,16 @@ defmodule AshAdmin.PageLive do
actor: actor,
authorize?: socket.assigns.authorizing
)
|> socket.assigns.api.read_one()
|> Ash.read_one(domain: socket.assigns.domain)
record =
socket.assigns.resource
|> to_one_relationships(socket.assigns.api)
|> to_one_relationships(socket.assigns.domain)
|> Enum.reduce(record, fn
rel, {:ok, record} ->
case socket.assigns.api.load(record, rel,
case Ash.load(record, rel,
actor: actor,
domain: socket.assigns.domain,
authorize?: socket.assigns.authorizing
) do
{:ok, record} ->
@ -345,12 +346,12 @@ defmodule AshAdmin.PageLive do
|> assign(:params, params)}
end
defp to_one_relationships(resource, api) do
defp to_one_relationships(resource, domain) do
resource
|> Ash.Resource.Info.relationships()
|> Enum.filter(fn relationship ->
api = relationship.api || api
AshAdmin.Api.show?(api) && relationship.cardinality == :one
domain = relationship.domain || domain || Ash.Resource.Info.domain(relationship.destination)
AshAdmin.Domain.show?(domain) && relationship.cardinality == :one
end)
|> Enum.map(& &1.name)
end
@ -391,23 +392,23 @@ defmodule AshAdmin.PageLive do
def handle_event(
"set_actor",
%{"resource" => resource, "api" => api, "pkey" => primary_key},
%{"resource" => resource, "domain" => domain, "pkey" => primary_key},
socket
)
when not is_nil(resource) and not is_nil(api) do
when not is_nil(resource) and not is_nil(domain) do
resource = Module.concat([resource])
case decode_primary_key(resource, primary_key) do
{:ok, pkey_filter} ->
api = Module.concat([api])
domain = Module.concat([domain])
action = AshAdmin.Helpers.primary_action(resource, :read)
actor =
resource
|> Ash.Query.filter(^pkey_filter)
|> api.read_one!(action: action, authorize?: false)
|> Ash.read_one!(action: action, authorize?: false, domain: domain)
api_name = AshAdmin.Api.name(api)
domain_name = AshAdmin.Domain.name(domain)
resource_name = AshAdmin.Resource.name(resource)
{:noreply,
@ -418,10 +419,10 @@ defmodule AshAdmin.PageLive do
resource: to_string(resource_name),
primary_key: encode_primary_key(actor),
action: to_string(action.name),
api: to_string(api_name)
domain: to_string(domain_name)
}
)
|> assign(actor: actor, actor_api: api)}
|> assign(actor: actor, actor_domain: domain)}
end
end

View file

@ -59,7 +59,7 @@ defmodule AshAdmin.Resource do
type: {:list, :string},
doc: """
For resources that use ash_postgres' polymorphism capabilities, you can provide a list of tables that should be available to
select. These will be added to the list of derivable tables based on scanning all APIs and resources provided to ash_admin.
select. These will be added to the list of derivable tables based on scanning all domains and resources provided to ash_admin.
"""
],
polymorphic_actions: [
@ -98,17 +98,17 @@ defmodule AshAdmin.Resource do
transformers: [AshAdmin.Resource.Transformers.ValidateTableColumns]
@moduledoc """
An API extension to alter the behaviour of a resource in the admin UI.
A resource extension to alter the behaviour of a resource in the admin UI.
"""
def polymorphic?(resource, apis) do
polymorphic_tables(resource, apis) not in [nil, []]
def polymorphic?(resource, domains) do
polymorphic_tables(resource, domains) not in [nil, []]
end
def polymorphic_tables(resource, apis) do
def polymorphic_tables(resource, domains) do
resource
|> Spark.Dsl.Extension.get_opt([:admin], :polymorphic_tables, [], true)
|> Enum.concat(find_polymorphic_tables(resource, apis))
|> Enum.concat(find_polymorphic_tables(resource, domains))
|> Enum.uniq()
end
@ -187,9 +187,9 @@ defmodule AshAdmin.Resource do
end)
end
defp find_polymorphic_tables(resource, apis) do
apis
|> Enum.flat_map(&Ash.Api.Info.resources/1)
defp find_polymorphic_tables(resource, domains) do
domains
|> Enum.flat_map(&Ash.Domain.Info.resources/1)
|> Enum.flat_map(&Ash.Resource.Info.relationships/1)
|> Enum.filter(&(&1.destination == resource))
|> Enum.map(& &1.context[:data_layer][:table])

View file

@ -120,7 +120,7 @@ defmodule AshAdmin.Router do
"actor_resource",
"actor_primary_key",
"actor_action",
"actor_api",
"actor_domain",
"actor_authorizing",
"actor_paused"
]

View file

@ -7,7 +7,7 @@ defmodule AshAdmin.SessionPlug do
"actor_resource",
"actor_primary_key",
"actor_action",
"actor_api",
"actor_domain",
"actor_authorizing",
"actor_paused"
]

17
mix.exs
View file

@ -61,7 +61,7 @@ defmodule AshAdmin.MixProject do
logo: "logos/small-logo.png",
extras: [
"README.md",
"documentation/dsls/DSL:-AshAdmin.Api.md",
"documentation/dsls/DSL:-AshAdmin.Domain.md",
"documentation/dsls/DSL:-AshAdmin.Resource.md"
],
groups_for_extras: [
@ -76,7 +76,7 @@ defmodule AshAdmin.MixProject do
defp aliases() do
[
generate_migrations:
"ash_postgres.generate_migrations --apis Demo.Accounts.Api,Demo.Tickets.Api --snapshot-path dev/resource_snapshots --migration-path dev --drop-columns",
"ash_postgres.generate_migrations --domains Demo.Accounts.Domain,Demo.Tickets.Domain --snapshot-path dev/resource_snapshots --migration-path dev --drop-columns",
credo: "credo --strict",
migrate: "ash_postgres.migrate --migrations-path dev/repo/migrations",
migrate_tenants: "ash_postgres.migrate --migrations-path dev/repo/tenant_migrations",
@ -91,13 +91,13 @@ defmodule AshAdmin.MixProject do
"spark.cheat_sheets_in_search"
],
test: ["setup", "test"],
"spark.formatter": "spark.formatter --extensions AshAdmin.Api,AshAdmin.Resource",
"spark.formatter": "spark.formatter --extensions AshAdmin.Domain,AshAdmin.Resource",
"assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
"assets.build": ["tailwind default", "esbuild default"],
"assets.deploy": ["tailwind default --minify", "esbuild default --minify", "phx.digest"],
"spark.cheat_sheets_in_search":
"spark.cheat_sheets_in_search --extensions AshAdmin.Api,AshAdmin.Resource",
"spark.cheat_sheets": "spark.cheat_sheets --extensions AshAdmin.Api,AshAdmin.Resource"
"spark.cheat_sheets_in_search --extensions AshAdmin.Domain,AshAdmin.Resource",
"spark.cheat_sheets": "spark.cheat_sheets --extensions AshAdmin.Domain,AshAdmin.Resource"
]
end
@ -112,8 +112,8 @@ defmodule AshAdmin.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:ash, "~> 2.0"},
{:ash_phoenix, "~> 1.1"},
{:ash, "~> 3.0.0-rc.0"},
{:ash_phoenix, "~> 2.0-rc.0"},
{:phoenix_view, "~> 2.0"},
{:phoenix, "~> 1.7"},
{:phoenix_live_view, "~> 0.19"},
@ -122,11 +122,12 @@ defmodule AshAdmin.MixProject do
{:tails, "~> 0.1"},
{:gettext, "~> 0.20"},
# Dev dependencies
{:simple_sat, "~> 0.1", only: [:dev, :test]},
{:esbuild, "~> 0.7", only: [:dev, :test]},
{:tailwind, "~> 0.2.0", only: [:dev, :test]},
{:plug_cowboy, "~> 2.0", only: [:dev, :test]},
{:phoenix_live_reload, "~> 1.2", only: [:dev, :test]},
{:ash_postgres, "~> 1.0"},
{:ash_postgres, "~> 2.0-rc.0"},
{:git_ops, "~> 2.4", only: [:dev, :test]},
{:ex_doc, "~> 0.23", only: [:dev, :test], runtime: false},
{:ex_check, "~> 0.14", only: [:dev, :test]},

View file

@ -1,7 +1,7 @@
%{
"ash": {:hex, :ash, "2.20.2", "fe0f948c22d00981826473e18cfef56b36d6761e0bf5f0079e7a5f46c8313be1", [: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: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [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]}, {:reactor, "~> 0.6", [hex: :reactor, repo: "hexpm", optional: false]}, {:spark, ">= 1.1.55 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a845c9924ae4d37cfa0cc5c86227196f3f244336376507565f3a081eecdeae3e"},
"ash_phoenix": {:hex, :ash_phoenix, "1.3.4", "8b38b5822cb68e1b8a1e9ccf947331318fcb7e8df53da841b229b3305aa6c0dd", [:mix], [{:ash, "~> 2.16", [hex: :ash, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "3d13fe54887c84cd89c7106aa05b7bbc87498b2da53e055b21ae21173fb59823"},
"ash_postgres": {:hex, :ash_postgres, "1.5.19", "fa4fd9fda59891774837a1c81b03cc96a7bbb220fb4471c90d7948ba4c315af4", [:mix], [{:ash, ">= 2.19.6 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", "54469ee1ca9edc5916725a2b5c46bef9307321c8ef3b6eaed573e3fa47647c5f"},
"ash": {:hex, :ash, "3.0.0-rc.3", "9d2af1a4baef19ef0ef8a7f4bbdbbd640d54d62b484cdfd42e583d88c566115f", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [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: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.8", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.1.7 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "83a16dace30aabb1f623e6f1b405cfe1af14f87a1411b1af9bbc745cd968a373"},
"ash_phoenix": {:hex, :ash_phoenix, "2.0.0-rc.1", "1e147bbf7d9ea077283ec7f8eef74e4c965cb524c773a691f3c79d87ca1ede0c", [:mix], [{:ash, "~> 3.0.0-rc.0", [hex: :ash, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "387b6c35c2b35b5b114306dec7095ca5d3fab94df03b30ad58bb69f8ea61e609"},
"ash_postgres": {:hex, :ash_postgres, "2.0.0-rc.2", "440760839f56c96c1e351056c724fd5464783d6ea3efeb980ce0875b57b20b7d", [:mix], [{:ash, "~> 3.0.0-rc.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", "d8c7182e24a235980b11b6047ee77fa2e0072b5352a37e60063dee4c91d746e7"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
@ -11,7 +11,7 @@
"credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 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", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
@ -20,27 +20,27 @@
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"},
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
"ex_check": {:hex, :ex_check, "0.15.0", "074b94c02de11c37bba1ca82ae5cc4926e6ccee862e57a485b6ba60fca2d8dc1", [:mix], [], "hexpm", "33848031a0c7e4209c3b4369ce154019788b5219956220c35ca5474299fb6a0e"},
"ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"},
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
"ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"},
"excoveralls": {:hex, :excoveralls, "0.18.0", "b92497e69465dc51bc37a6422226ee690ab437e4c06877e836f1c18daeb35da9", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1109bb911f3cb583401760be49c02cbbd16aed66ea9509fc5479335d284da60b"},
"expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"floki": {:hex, :floki, "0.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"},
"gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"},
"expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"floki": {:hex, :floki, "0.36.1", "712b7f2ba19a4d5a47dfe3e74d81876c95bbcbee44fe551f0af3d2a388abb3da", [:mix], [], "hexpm", "21ba57abb8204bcc70c439b423fc0dd9f0286de67dc82773a14b0200ada0995f"},
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.6.0", "e0791ee1cf5db03f2c61b7ebd70e2e95cba2bb9b9793011f26609f22c0900087", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "b98fca849b18aaf490f4ac7d1dd8c6c469b0cc3e6632562d366cab095e666ffe"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [: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", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [: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", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"phoenix": {:hex, :phoenix, "1.7.11", "1d88fc6b05ab0c735b250932c4e6e33bfa1c186f76dcf623d8dd52f07d6379c7", [: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.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [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", "b1ec57f2e40316b306708fe59b92a16b9f6f4bf50ccfa41aa8c7feb79e0ec02a"},
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [: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", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.12", "3f4b5849b8018023c01e41a7da2e6c986222cc3f0282858f8af11221638645cb", [: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 or ~> 4.0", [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]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ae3a143cc33325f3a4c192b7da1726e6665e154c50e1461af4cd7d561ccfd9ab"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.14", "70fa101aa0539e81bed4238777498f6215e9dda3461bdaa067cad6908110c364", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {: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 or ~> 4.0", [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]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "82f6d006c5264f979ed5eb75593d808bbe39020f20df2e78426f4f2d570e2402"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"},
@ -50,15 +50,17 @@
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
"postgrex": {:hex, :postgrex, "0.17.5", "0483d054938a8dc069b21bdd636bf56c487404c241ce6c319c1f43588246b281", [: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", "50b8b11afbb2c4095a3ba675b4f055c416d0f3d7de6633a595fc131a828a67eb"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"reactor": {:hex, :reactor, "0.7.0", "fb76d23d95829b28ac9b9d654620c43c890c6a32ea26ac13086c48540b34e8c5", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 1.0", [hex: :spark, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4310da820d753aafd7dc4ee8cc687b84565dd6d9536e38806ee211da792178fd"},
"reactor": {:hex, :reactor, "0.8.1", "1aec71d16083901277727c8162f6dd0f07e80f5ca98911b6ef4f2c95e6e62758", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ae3936d97a3e4a316744f70c77b85345b08b70da334024c26e6b5eb8ede1246b"},
"simple_sat": {:hex, :simple_sat, "0.1.1", "68a5ebe6f6d5956bd806e4881c495692c14580a2f1a4420488985abd0fba2119", [:mix], [], "hexpm", "63571218f92ff029838df7645eb8f0c38df8ed60d2d14578412a8d142a94471e"},
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"sourceror": {:hex, :sourceror, "1.0.2", "c5e86fdc14881f797749d1fe5df017ca66727a8146e7ee3e736605a3df78f3e6", [:mix], [], "hexpm", "832335e87d0913658f129d58b2a7dc0490ddd4487b02de6d85bca0169ec2bd79"},
"spark": {:hex, :spark, "1.1.55", "d20c3f899b23d841add29edc912ffab4463d3bb801bc73448738631389291d2e", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "bbc15a4223d8e610c81ceca825d5d0bae3738d1c4ac4dbb1061749966776c3f1"},
"spark": {:hex, :spark, "2.1.10", "f32cf6a0231f7a8a128889ee82682d53743c05b9c5b26392fefc0513b9be06d7", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "86a88091a754ea2a4ed6dbc919e57e60828112b3552de14adb83ee20064813a2"},
"splode": {:hex, :splode, "0.2.1", "020079ec06c9e00f8b6586852e781b5e07aee6ba588f3f45dd993831c87b0511", [:mix], [], "hexpm", "d232a933666061fe1f659d9906042fa94b9b393bb1129a4fde6fa680033b2611"},
"stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"},
"tails": {:hex, :tails, "0.1.7", "d77a89c2faea02237d78ea81824c1362dbc3cfa4e2a203be7808617ae47bb5eb", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d5ae73c55d181ab1353f2c49aea2ef540edfa6ec6be65a89e8a392ba15a94b21"},
"tails": {:hex, :tails, "0.1.10", "2edae202ad8b73bc9cb62fac20dec280549d7d7817e50027698c2f1de21bbc34", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "20ab7dd4d75e946daa67b83c1a392d9d87f7b1d3620ccbfe0ba2fb1debabd457"},
"tailwind": {:hex, :tailwind, "0.2.2", "9e27288b568ede1d88517e8c61259bc214a12d7eed271e102db4c93fcca9b2cd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "ccfb5025179ea307f7f899d1bb3905cd0ac9f687ed77feebc8f67bdca78565c4"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [: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", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"},
"websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [: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", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"},
}

View file

@ -1,13 +0,0 @@
defmodule AshAdmin.Test.Api do
@moduledoc false
use Ash.Api,
extensions: [AshAdmin.Api]
admin do
show? true
end
resources do
registry(AshAdmin.Test.Registry)
end
end

13
test/support/domain.ex Normal file
View file

@ -0,0 +1,13 @@
defmodule AshAdmin.Test.Domain do
@moduledoc false
use Ash.Domain,
extensions: [AshAdmin.Domain]
admin do
show? true
end
resources do
resource(AshAdmin.Test.Post)
end
end

View file

@ -1,8 +0,0 @@
defmodule AshAdmin.Test.Registry do
@moduledoc false
use Ash.Registry
entries do
entry(AshAdmin.Test.Post)
end
end

View file

@ -1,6 +1,7 @@
defmodule AshAdmin.Test.Post do
@moduledoc false
use Ash.Resource,
domain: Demo.Tickets.Domain,
data_layer: Ash.DataLayer.Ets
attributes do
@ -8,6 +9,7 @@ defmodule AshAdmin.Test.Post do
attribute :body, :string do
allow_nil?(false)
public? true
end
end
end