ash/lib/ash.ex

136 lines
4.3 KiB
Elixir
Raw Normal View History

2019-10-03 16:08:36 +13:00
defmodule Ash do
2020-10-10 03:13:44 +13:00
@moduledoc " Types and simple helpers for Ash"
2020-06-02 17:47:25 +12:00
alias Ash.Resource.Actions.{Create, Destroy, Read, Update}
alias Ash.Resource.Relationships.{BelongsTo, HasMany, HasOne, ManyToMany}
2020-01-14 07:16:24 +13:00
@type action :: Create.t() | Read.t() | Update.t() | Destroy.t()
@type action_type :: :read | :create | :update | :destroy
@type actor :: Ash.record()
@type aggregate :: Ash.Query.Aggregate.t() | Ash.Resource.Aggregate.t()
2020-08-09 05:55:36 +12:00
@type aggregate_kind :: Ash.Query.Aggregate.kind()
@type api :: module
@type attribute :: Ash.Resource.Attribute.t()
@type calculation :: Ash.Resource.Calculation.t()
2019-10-07 09:36:06 +13:00
@type cardinality_many_relationship() :: HasMany.t() | ManyToMany.t()
@type cardinality_one_relationship() :: HasOne.t() | BelongsTo.t()
@type changeset :: Ash.Changeset.t()
2019-12-05 20:18:13 +13:00
@type data_layer :: module
@type data_layer_query :: struct
2019-10-07 09:36:06 +13:00
@type error :: struct
2020-08-09 05:55:36 +12:00
@type filter :: Ash.Filter.t()
2019-12-23 17:28:40 +13:00
@type params :: Keyword.t()
@type primary_key :: record() | map | term
@type query :: Ash.Query.t()
@type record :: struct
@type relationship :: cardinality_one_relationship() | cardinality_many_relationship()
@type relationship_cardinality :: :many | :one
@type resource :: module
@type side_loads :: term
2020-10-12 16:55:47 +13:00
@type page :: Ash.Page.Keyset.t() | Ash.Page.Offset.t()
@type sort_order ::
:asc | :desc | :asc_nils_first | :asc_nils_last | :desc_nils_first | :desc_nils_last
@type sort :: list(atom | {atom, sort_order})
@type validation :: Ash.Resource.Validation.t()
2020-10-15 17:54:02 +13:00
@type notification :: Ash.Notifier.Notification.t()
2020-05-21 10:59:58 +12:00
require Ash.Dsl.Extension
2019-10-04 15:33:55 +13:00
def implements_behaviour?(module, behaviour) do
:attributes
|> module.module_info()
|> Enum.flat_map(fn
{:behaviour, value} -> List.wrap(value)
_ -> []
end)
|> Enum.any?(&(&1 == behaviour))
2021-01-14 19:17:26 +13:00
rescue
_ ->
false
end
2020-09-04 16:59:32 +12:00
def uuid do
Ecto.UUID.generate()
end
feat: freeform expressions feat: validatiosn in actions feat: query arguments feat: add `Ash.Query.for_read/3` feat: return changeset with API errors feat: add case insensitive string `CiString`/`:ci_string` feat: support `context/1` and `arg/1` in filter templates feat: support targeting notifications with the `for` option feat: add `ago/2` query function feat: add basic arithmetic operators (+, *, -, /) feat: `sensitive?` option for attributes feat: `sensitive?` option for arguments feat: `private` arguments, which can’t be set using `for_<action>` feat: add `prevent_change` which will erase changes just before the changeset is committed feat: add `match?` validation that supports a custom error message feat: add `interval` type to support `ago/2` function feat: add `url_encoded_binary` type feat: add `function` type improvement: `changing?` is now a validation improvement: add `Transformer.get_persisted/3` improvement: add `api` field to `Notification` improvement: standardize errors, add `to_error_class` improvement: use `Comp` everywhere Improvement: use action on changeset if set by `for_<action_type>` improvement: `action_failed?` field on change sets improvement: remove ability for data layers to add operators (for now at least) Improvement: Changeset.apply_attributes/2 now returns an error tuple Improvement: add a bunch of new/informative errors improvement: runtime filter now uses left join logic (a naive implementation of it) improvement: support more filter templates in resources Improvement: basic/naive type system for operators/functions Fix: properly expand module aliases for options w/o compile time dependency chore(engine): track changeset changes for the request with `manage_changeset?: true`
2021-01-22 09:21:58 +13:00
# A restricted version of `:erlang.binary_to_term/2` that forbids
# *executable* terms, such as anonymous functions.
# The `opts` are given to the underlying `:erlang.binary_to_term/2`
# call, with an empty list as a default.
# By default this function does not restrict atoms, as an atom
# interned in one node may not yet have been interned on another
# (except for releases, which preload all code).
# If you want to avoid atoms from being created, then you can pass
# `[:safe]` as options, as that will also enable the safety mechanisms
# from `:erlang.binary_to_term/2` itself.
# Ripped from https://github.com/elixir-plug/plug_crypto/blob/v1.2.0/lib/plug/crypto.ex
# sobelow_skip ["Misc.BinToTerm"]
def non_executable_binary_to_term(binary, opts \\ []) when is_binary(binary) do
feat: freeform expressions feat: validatiosn in actions feat: query arguments feat: add `Ash.Query.for_read/3` feat: return changeset with API errors feat: add case insensitive string `CiString`/`:ci_string` feat: support `context/1` and `arg/1` in filter templates feat: support targeting notifications with the `for` option feat: add `ago/2` query function feat: add basic arithmetic operators (+, *, -, /) feat: `sensitive?` option for attributes feat: `sensitive?` option for arguments feat: `private` arguments, which can’t be set using `for_<action>` feat: add `prevent_change` which will erase changes just before the changeset is committed feat: add `match?` validation that supports a custom error message feat: add `interval` type to support `ago/2` function feat: add `url_encoded_binary` type feat: add `function` type improvement: `changing?` is now a validation improvement: add `Transformer.get_persisted/3` improvement: add `api` field to `Notification` improvement: standardize errors, add `to_error_class` improvement: use `Comp` everywhere Improvement: use action on changeset if set by `for_<action_type>` improvement: `action_failed?` field on change sets improvement: remove ability for data layers to add operators (for now at least) Improvement: Changeset.apply_attributes/2 now returns an error tuple Improvement: add a bunch of new/informative errors improvement: runtime filter now uses left join logic (a naive implementation of it) improvement: support more filter templates in resources Improvement: basic/naive type system for operators/functions Fix: properly expand module aliases for options w/o compile time dependency chore(engine): track changeset changes for the request with `manage_changeset?: true`
2021-01-22 09:21:58 +13:00
term = :erlang.binary_to_term(binary, opts)
non_executable_terms(term)
term
end
defp non_executable_terms(list) when is_list(list) do
non_executable_list(list)
end
defp non_executable_terms(tuple) when is_tuple(tuple) do
non_executable_tuple(tuple, tuple_size(tuple))
end
defp non_executable_terms(map) when is_map(map) do
folder = fn key, value, acc ->
non_executable_terms(key)
non_executable_terms(value)
acc
end
:maps.fold(folder, map, map)
end
defp non_executable_terms(other)
when is_atom(other) or is_number(other) or is_bitstring(other) or is_pid(other) or
is_reference(other) do
other
end
defp non_executable_terms(other) do
raise ArgumentError,
"cannot deserialize #{inspect(other)}, the term is not safe for deserialization"
end
defp non_executable_list([]), do: :ok
defp non_executable_list([h | t]) when is_list(t) do
non_executable_terms(h)
non_executable_list(t)
end
defp non_executable_list([h | t]) do
non_executable_terms(h)
non_executable_terms(t)
end
defp non_executable_tuple(_tuple, 0), do: :ok
defp non_executable_tuple(tuple, n) do
non_executable_terms(:erlang.element(n, tuple))
non_executable_tuple(tuple, n - 1)
end
@doc "Returns all extensions of a resource or api"
@spec extensions(resource() | api()) :: [module]
def extensions(resource) do
Ash.Dsl.Extension.get_persisted(resource, :extensions)
end
@spec try_compile(term) :: :ok
def try_compile(module) when is_atom(module) do
Code.ensure_loaded(module)
:ok
end
feat: freeform expressions feat: validatiosn in actions feat: query arguments feat: add `Ash.Query.for_read/3` feat: return changeset with API errors feat: add case insensitive string `CiString`/`:ci_string` feat: support `context/1` and `arg/1` in filter templates feat: support targeting notifications with the `for` option feat: add `ago/2` query function feat: add basic arithmetic operators (+, *, -, /) feat: `sensitive?` option for attributes feat: `sensitive?` option for arguments feat: `private` arguments, which can’t be set using `for_<action>` feat: add `prevent_change` which will erase changes just before the changeset is committed feat: add `match?` validation that supports a custom error message feat: add `interval` type to support `ago/2` function feat: add `url_encoded_binary` type feat: add `function` type improvement: `changing?` is now a validation improvement: add `Transformer.get_persisted/3` improvement: add `api` field to `Notification` improvement: standardize errors, add `to_error_class` improvement: use `Comp` everywhere Improvement: use action on changeset if set by `for_<action_type>` improvement: `action_failed?` field on change sets improvement: remove ability for data layers to add operators (for now at least) Improvement: Changeset.apply_attributes/2 now returns an error tuple Improvement: add a bunch of new/informative errors improvement: runtime filter now uses left join logic (a naive implementation of it) improvement: support more filter templates in resources Improvement: basic/naive type system for operators/functions Fix: properly expand module aliases for options w/o compile time dependency chore(engine): track changeset changes for the request with `manage_changeset?: true`
2021-01-22 09:21:58 +13:00
def try_compile(_), do: :ok
2019-10-03 16:08:36 +13:00
end