Packages
ash_admin
0.13.11
1.1.0
1.0.0-rc.0
0.14.0
0.13.26
0.13.25
0.13.24
0.13.23
0.13.22
0.13.21
0.13.20
0.13.19
0.13.18
0.13.17
0.13.16
0.13.15
0.13.14
0.13.13
0.13.12
0.13.11
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.11
0.11.10
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.12
0.10.11
0.10.10
retired
0.10.10-rc.1
0.10.10-rc.0
0.10.9
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0-rc.2
0.6.0-rc.1
0.6.0-rc.0
0.5.2
0.5.1-rc.0
0.5.0
0.4.5-rc.0
0.4.4
0.4.2
0.4.0
0.3.0-rc.0
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16-rc.1
0.2.16-rc.0
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.1
0.1.6
0.1.5
0.1.3
0.1.2
0.1.0
A super-admin UI for Ash Framework, built with Phoenix LiveView.
Current section
Files
Jump to
Current section
Files
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."
],
generic_actions: [
type: {:list, :atom},
doc:
"A list of generic 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 domains 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 represented as a MFA. For example: `updated_at: {Timex, :format!, ["{0D}-{0M}-{YYYY} {h12}:{m} {AM}"]}`. Datatable pages format all given fields. Show and Update pages format given read-only fields of types `Ash.Type.Date`, `Ash.Type.DateTime`, `Ash.Type.Time`, `Ash.Type.NaiveDatetime`, `Ash.Type.UtcDatetime` and `Ash.Type.UtcDatetimeUsec`.
"""
],
relationship_display_fields: [
type: {:list, :atom},
doc:
"The list of attributes to render when this resource is shown as a relationship on another resource's 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."
],
show_calculations: [
type: {:list, :atom},
doc:
"A list of calculation that can be calculate when this resource is shown. By default, all calculations are included."
],
label_field: [
type: :atom,
doc:
"The field to use as the label when the resource appears in a relationship select or typeahead field on another resource's form."
],
relationship_select_max_items: [
type: :integer,
default: 50,
doc:
"The maximum number of items to show in a select field when this resource is shown as a relationship on another resource's form. If the number of related resources is higher, a typeahead selector will be used."
]
]
}
use Spark.Dsl.Extension,
sections: [@admin],
transformers: [
AshAdmin.Resource.Transformers.ValidateTableColumns,
AshAdmin.Resource.Transformers.AddPositionSortCalculation
],
verifiers: [
AshAdmin.Resource.Verifiers.VerifyFileArgumentsExist
]
@moduledoc """
A resource extension to alter the behaviour of a resource in the admin UI.
"""
def polymorphic?(resource, domains) do
polymorphic_tables(resource, domains) not in [nil, []]
end
def polymorphic_tables(resource, domains) do
resource
|> Spark.Dsl.Extension.get_opt([:admin], :polymorphic_tables, [], true)
|> Enum.concat(find_polymorphic_tables(resource, domains))
|> 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 label_field(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :label_field, nil, true)
end
def relationship_select_max_items(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :relationship_select_max_items, 50, 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 generic_actions(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :generic_actions, nil, true) ||
actions_with_primary_first(resource, :action)
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 show_calculations(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :show_calculations, [], true)
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, domains) do
domains
|> Enum.flat_map(&AshAdmin.Domain.show_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(&(!Map.get(&1, :primary?)))
|> Enum.map(& &1.name)
end
end