You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

403 lines
9.8 KiB

defmodule DecentralisedBookIndex.Metadata.Book do
use Ash.Resource,
otp_app: :decentralised_book_index,
domain: DecentralisedBookIndex.Metadata,
data_layer: AshPostgres.DataLayer,
extensions: [AshJsonApi.Resource],
authorizers: [Ash.Policy.Authorizer]
require Ash.Query
alias DecentralisedBookIndex.Metadata
@brief_description_length 20
@brief_description_max_length @brief_description_length + 3
json_api do
type "book"
end
postgres do
table "books"
repo DecentralisedBookIndex.Repo
custom_indexes do
index "title gin_trgm_ops", name: "book_title_gin_index", using: "GIN"
end
end
resource do
description "A book's metadata."
end
actions do
defaults [:read, :destroy]
create :create do
primary? true
accept [
:title,
:description,
:format,
:language,
:page_count,
:published,
:publisher_id,
:cover_image_url,
:book_editions_registry_id,
:dbi_server_id
]
argument :bids, {:array, :map}
argument :author_roles, {:array, :map}
change fn changeset, context ->
actor = Map.get(context, :actor, nil)
registry_id = Ash.Changeset.get_attribute(changeset, :book_editions_registry_id)
if registry_id == nil do
registry = DecentralisedBookIndex.Metadata.create_book_editions_registry!(actor: actor)
Ash.Changeset.force_change_attribute(changeset, :book_editions_registry_id, registry.id)
else
changeset
end
end
change manage_relationship(:bids, type: :direct_control, order_is_key: :order)
change manage_relationship(:author_roles, type: :direct_control, order_is_key: :order)
end
create :sync_create do
accept [
:id,
:title,
:description,
:format,
:language,
:page_count,
:published,
:publisher_id,
:cover_image_url,
:book_editions_registry_id,
:inserted_at,
:updated_at,
:dbi_server_id
]
argument :bids, {:array, :map}
argument :author_roles, {:array, :map}
change fn changeset, context ->
registry_id = Ash.Changeset.get_attribute(changeset, :book_editions_registry_id)
if registry_id == nil do
registry =
DecentralisedBookIndex.Metadata.create_book_editions_registry!(authorize?: false)
Ash.Changeset.force_change_attribute(changeset, :book_editions_registry_id, registry.id)
else
changeset
end
end
change manage_relationship(:bids, type: :direct_control, order_is_key: :order)
change manage_relationship(:author_roles, type: :direct_control, order_is_key: :order)
end
create :add_book_to_related_editions_registry do
accept [
:title,
:description,
:format,
:language,
:page_count,
:published,
:publisher_id,
:cover_image_url
]
argument :related_book_id, :uuid do
allow_nil? false
end
argument :bids, {:array, :map}
argument :author_roles, {:array, :map}
change fn changeset, context ->
related_book_id = changeset.arguments.related_book_id
if related_book_id == nil do
Ash.Changeset.add_error(changeset, :related_book_id, "Related book is empty")
else
{:ok, related_book} = DecentralisedBookIndex.Metadata.get_book_by_id(related_book_id)
Ash.Changeset.force_change_attribute(
changeset,
:book_editions_registry_id,
related_book.book_editions_registry_id
)
end
end
change manage_relationship(:bids, type: :direct_control, order_is_key: :order)
change manage_relationship(:author_roles, type: :direct_control, order_is_key: :order)
end
read :by_id do
description "Return the Book by its id."
argument :id, :uuid, allow_nil?: false
get? true
filter expr(id == ^arg(:id))
end
read :by_bid do
argument :type, :string, allow_nil?: false
argument :bid, :string, allow_nil?: false
get? true
filter expr(exists(bids, type == ^arg(:type) and bid == ^arg(:bid)))
end
read :get_alternative_editions do
argument :book, :struct, allow_nil?: false
prepare fn query, context ->
book = query.arguments.book
DecentralisedBookIndex.Metadata.Book
|> Ash.Query.filter(
book_editions_registry_id == ^book.book_editions_registry_id and
id != ^book.id
)
end
end
read :get_author_books do
argument :author, :struct, allow_nil?: false
prepare fn query, context ->
author = query.arguments.author
{:ok, author_ids} = Metadata.get_author_ids(author)
Metadata.Book
|> Ash.Query.filter(expr(exists(author_roles, author_id in ^author_ids)))
end
end
read :search do
description "Return a list of Books, optionally filtering by title."
argument :query, :ci_string do
description "Return Books, which titles includes the query."
constraints allow_empty?: true
default ""
end
filter expr(contains(title, ^arg(:query)))
pagination offset?: true, default_limit: 10
end
read :search_by_bid do
description "Return a list of Books, optionally filtering by BookId."
argument :type, :string, allow_nil?: false
argument :bid, :string, allow_nil?: false
filter expr(exists(bids, type == ^arg(:type) and contains(bid, ^arg(:bid))))
pagination offset?: true, default_limit: 10
end
update :update do
primary? true
require_atomic? false
accept [
:title,
:description,
:format,
:language,
:page_count,
:published,
:publisher_id,
:cover_image_url,
:book_editions_registry_id
]
argument :bids, {:array, :map}
argument :author_roles, {:array, :map}
change fn changeset, _ ->
registry_id = Ash.Changeset.get_attribute(changeset, :book_editions_registry_id)
if registry_id == nil do
{:ok, registry} = DecentralisedBookIndex.Metadata.create_book_editions_registry()
Ash.Changeset.force_change_attribute(changeset, :book_editions_registry_id, registry.id)
else
changeset
end
end
change manage_relationship(:bids, type: :direct_control, order_is_key: :order)
change manage_relationship(:author_roles, type: :direct_control, order_is_key: :order)
end
update :sync do
require_atomic? false
accept [
:id,
:title,
:description,
:format,
:language,
:page_count,
:published,
:publisher_id,
:cover_image_url,
:book_editions_registry_id,
:inserted_at,
:updated_at,
:dbi_server_id
]
argument :bids, {:array, :map}
argument :author_roles, {:array, :map}
change fn changeset, _ ->
registry_id = Ash.Changeset.get_attribute(changeset, :book_editions_registry_id)
if registry_id == nil do
{:ok, registry} = DecentralisedBookIndex.Metadata.create_book_editions_registry()
Ash.Changeset.force_change_attribute(changeset, :book_editions_registry_id, registry.id)
else
changeset
end
end
change manage_relationship(:bids, type: :direct_control, order_is_key: :order)
change manage_relationship(:author_roles, type: :direct_control, order_is_key: :order)
end
update :assign_cover_image do
accept [:cover_image_url]
end
update :assign_dbi_server_id do
accept [:dbi_server_id]
end
end
policies do
bypass actor_attribute_equals(:role, :admin) do
authorize_if always()
end
policy action_type(:read) do
authorize_if always()
end
policy action_type(:create) do
authorize_if actor_attribute_equals(:role, :moderator)
end
policy action_type(:update) do
authorize_if actor_attribute_equals(:role, :moderator)
end
policy action_type(:destroy) do
authorize_if actor_attribute_equals(:role, :admin)
end
end
validations do
validate numericality(:page_count, greater_than: 0)
end
attributes do
uuid_primary_key :id, writable?: true
attribute :title, :string do
allow_nil? false
public? true
end
attribute :description, :string do
allow_nil? false
public? true
end
attribute :published, :date do
allow_nil? false
public? true
end
attribute :language, :string do
allow_nil? false
public? true
end
attribute :format, :string do
allow_nil? false
public? true
end
attribute :page_count, :integer do
allow_nil? false
public? true
end
attribute :cover_image_url, :string do
allow_nil? true
public? true
end
timestamps do
writable? true
public? true
end
end
relationships do
belongs_to :dbi_server, Metadata.DBIServer do
public? true
end
belongs_to :book_editions_registry, Metadata.BookEditionsRegistry do
public? true
end
belongs_to :publisher, Metadata.Publisher do
allow_nil? false
public? true
end
has_many :author_roles, Metadata.AuthorRole do
sort order: :asc
public? true
end
has_many :bids, Metadata.BookId do
sort order: :asc
public? true
end
end
calculations do
calculate :brief_description,
:string,
expr(
if(string_length(description) <= @brief_description_max_length) do
description
else
fragment("substr(?, 1, ?)", description, @brief_description_length) <> "..."
end
)
end
end