Current section

Files

Jump to
ash_admin lib ash_admin resource resource.ex
Raw

lib/ash_admin/resource/resource.ex

defmodule AshAdmin.Resource do
@field %Spark.Dsl.Entity{
describe: "Declare non-default behavior for a specific attribute.",
name: :field,
schema: AshAdmin.Resource.Field.schema(),
target: AshAdmin.Resource.Field,
args: [:name]
}
@form %Spark.Dsl.Section{
describe: "Configure the appearance of fields in admin forms.",
name: :form,
entities: [
@field
]
}
@admin %Spark.Dsl.Section{
describe: "Configure the admin dashboard for a given resource.",
name: :admin,
sections: [
@form
],
schema: [
name: [
type: :string,
doc: "The proper name to use when this resource appears in the admin interface."
],
actor?: [
type: :boolean,
doc: "Whether or not this resource can be used as the actor for requests."
],
show_action: [
type: :atom,
doc:
"The action to use when linking to the resource/viewing a single record. Defaults to the primary read action."
],
read_actions: [
type: {:list, :atom},
doc:
"A list of read actions that can be used to show resource details. By default, all actions are included."
],
create_actions: [
type: {:list, :atom},
doc:
"A list of create actions that can create records. By default, all actions are included."
],
update_actions: [
type: {:list, :atom},
doc:
"A list of update actions that can be used to update records. By default, all actions are included."
],
destroy_actions: [
type: {:list, :atom},
doc:
"A list of destroy actions that can be used to destroy records. By default, all actions are included."
],
polymorphic_tables: [
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.
"""
],
polymorphic_actions: [
type: {:list, :atom},
doc: """
For resources that use ash_postgres' polymorphism capabilities, you can provide a list of actions that should require a table to be set.
If this is not set, then *all* actions will require tables.
"""
],
table_columns: [
type: {:list, :atom},
doc: "The list of attributes to render on the table view."
],
format_fields: [
type: {:list, :any},
doc: "The list of fields and their formats."
],
relationship_display_fields: [
type: {:list, :atom},
doc: "The list of attributes to render when it's shown as a relationship on a datatable"
],
resource_group: [
type: :atom,
doc: "The group in the top resource dropdown that the resource appears in."
],
show_sensitive_fields: [
type: {:list, :atom},
doc:
"The list of fields that should not be redacted in the admin UI even if they are marked as sensitive."
]
]
}
use Spark.Dsl.Extension,
sections: [@admin],
transformers: [AshAdmin.Resource.Transformers.ValidateTableColumns]
@moduledoc """
An API extension to alter the behaviour of a resource in the admin UI.
"""
def polymorphic?(resource, apis) do
polymorphic_tables(resource, apis) not in [nil, []]
end
def polymorphic_tables(resource, apis) do
resource
|> Spark.Dsl.Extension.get_opt([:admin], :polymorphic_tables, [], true)
|> Enum.concat(find_polymorphic_tables(resource, apis))
|> Enum.uniq()
end
def polymorphic_actions(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :polymorphic_actions, nil, true)
end
def relationship_display_fields(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :relationship_display_fields, nil, true)
end
def table_columns(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :table_columns, nil, true) ||
Enum.map(Ash.Resource.Info.attributes(resource), & &1.name)
end
def format_fields(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :format_fields, nil, true)
end
def name(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :name, nil, true) ||
resource
|> Module.split()
|> List.last()
end
def resource_group(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :resource_group, nil, true)
end
def show_sensitive_fields(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :show_sensitive_fields, [], true)
end
def actor?(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :actor?, false, true)
end
def read_actions(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :read_actions, nil, true) ||
actions_with_primary_first(resource, :read)
end
def create_actions(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :create_actions, nil, true) ||
actions_with_primary_first(resource, :create)
end
def update_actions(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :update_actions, nil, true) ||
actions_with_primary_first(resource, :update)
end
def destroy_actions(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :destroy_actions, nil, true) ||
actions_with_primary_first(resource, :destroy)
end
def show_action(resource) do
action = Spark.Dsl.Extension.get_opt(resource, [:admin], :show_action, false, [])
if action do
action
else
action = AshAdmin.Helpers.primary_action(resource, :read)
action && action.name
end
end
def fields(resource) do
Spark.Dsl.Extension.get_entities(resource, [:admin, :form])
end
def field(resource, name) do
resource
|> fields()
|> Enum.find(fn field ->
field.name == name
end)
end
defp find_polymorphic_tables(resource, apis) do
apis
|> Enum.flat_map(&Ash.Api.Info.resources/1)
|> Enum.flat_map(&Ash.Resource.Info.relationships/1)
|> Enum.filter(&(&1.destination == resource))
|> Enum.map(& &1.context[:data_layer][:table])
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
end
defp actions_with_primary_first(resource, type) do
resource
|> Ash.Resource.Info.actions()
|> Enum.filter(&(&1.type == type))
|> Enum.sort_by(&(!&1.primary?))
|> Enum.map(& &1.name)
end
end