working on defaults, updating error messages

This commit is contained in:
Zach Daniel 2019-12-08 01:21:09 -05:00
parent 941359bb8f
commit a2e007697a
No known key found for this signature in database
GPG key ID: A57053A671EE649E
19 changed files with 126 additions and 61 deletions

View file

@ -62,3 +62,4 @@ Ash is an open source project, and draws inspiration from similar ideas in other
* Validate that all relationships on all resources in the API have destinations *in* that API, or don't and add in logic to pretend those don't exist through the API. * Validate that all relationships on all resources in the API have destinations *in* that API, or don't and add in logic to pretend those don't exist through the API.
* Make authorization spit out informative errors (at least for developers) * Make authorization spit out informative errors (at least for developers)
* Use telemetry and/or some kind of hook system to add metrics * Use telemetry and/or some kind of hook system to add metrics
* Forbid impossible auth/creation situations (e.g "the id field is not exposed on a create action, and doesn't have a default, therefore writes will always fail.)

View file

@ -65,9 +65,23 @@ defmodule Ash.Actions.Create do
|> Ash.attributes() |> Ash.attributes()
|> Enum.map(& &1.name) |> Enum.map(& &1.name)
# Map.put_new(attributes, :id, Ecto.UUID.generate())
attributes_with_defaults =
resource
|> Ash.attributes()
|> Stream.filter(&(not is_nil(&1.default)))
|> Enum.reduce(attributes, fn attr, attributes ->
if Map.has_key?(attributes, attr.name) do
attributes
else
Map.put(attributes, attr.name, default(attr))
end
end)
resource resource
|> struct() |> struct()
|> Ecto.Changeset.cast(Map.put_new(attributes, :id, Ecto.UUID.generate()), allowed_keys) |> Ecto.Changeset.cast(attributes_with_defaults, allowed_keys)
|> case do |> case do
%{valid?: true} = changeset -> %{valid?: true} = changeset ->
{:ok, changeset} {:ok, changeset}
@ -78,6 +92,10 @@ defmodule Ash.Actions.Create do
end end
end end
defp default(%{default: {:constant, value}}), do: value
defp default(%{default: {mod, func}}), do: apply(mod, func, [])
defp default(%{default: function}), do: function.()
defp prepare_create_relationships(changeset, _resource, _relationships) do defp prepare_create_relationships(changeset, _resource, _relationships) do
{:ok, changeset} {:ok, changeset}
# relationships # relationships

View file

@ -41,7 +41,7 @@ defmodule Ash.Api do
``` ```
Then you can interact through that Api with the actions that those resources expose. Then you can interact through that Api with the actions that those resources expose.
For example: `MyApp.Api.create(OneResource, %{attributes: %{name: "thing"}}), or For example: `MyApp.Api.create(OneResource, %{attributes: %{name: "thing"}})`, or
`MyApp.Api.read(OneResource, %{filter: %{name: "thing"}})`. Corresponding actions must `MyApp.Api.read(OneResource, %{filter: %{name: "thing"}})`. Corresponding actions must
be defined in your resources in order to call them through the Api. be defined in your resources in order to call them through the Api.
""" """
@ -115,7 +115,7 @@ defmodule Ash.Api do
max_concurrency: max_concurrency:
"The total number of side loads (per side load nesting level, per ongoing side load) that can be running. Uses `System.schedulers_online/0` if unset.", "The total number of side loads (per side load nesting level, per ongoing side load) that can be running. Uses `System.schedulers_online/0` if unset.",
timeout: timeout:
"the maximum amount of time to wait (in milliseconds) without receiving a task reply. see `task.supervisor.async_stream/6", "the maximum amount of time to wait (in milliseconds) without receiving a task reply. see `task.supervisor.async_stream/6`",
shutdown: shutdown:
"an integer indicating the timeout value. Defaults to 5000 milliseconds" "an integer indicating the timeout value. Defaults to 5000 milliseconds"
], ],

View file

@ -29,7 +29,7 @@ defmodule Ash.Resource do
default_page_size: default_page_size:
"The default page size for any read action. If no page size is specified, this value is used.", "The default page size for any read action. If no page size is specified, this value is used.",
primary_key: primary_key:
"If true, a default `id` uuid primary key is used. If false, none is created. See the primary_key opts for info on specifying primary key options." "If true, a default `id` uuid primary key that autogenerates is used. If false, none is created. See the primary_key opts for info on specifying primary key options."
], ],
required: [:name, :type], required: [:name, :type],
defaults: [ defaults: [
@ -124,7 +124,12 @@ defmodule Ash.Resource do
def define_primary_key(mod, opts) do def define_primary_key(mod, opts) do
case opts[:primary_key] do case opts[:primary_key] do
true -> true ->
{:ok, attribute} = Ash.Resource.Attributes.Attribute.new(:id, :uuid, primary_key?: true) {:ok, attribute} =
Ash.Resource.Attributes.Attribute.new(:id, :uuid,
primary_key?: true,
default: &Ecto.UUID.generate/0
)
Module.put_attribute(mod, :attributes, attribute) Module.put_attribute(mod, :attributes, attribute)
false -> false ->

View file

@ -28,7 +28,7 @@ defmodule Ash.Resource.Actions do
end end
end end
@doc "Returns an `allow` rule. See `Ash.Authorization.Rule.new/2 for more." @doc "Returns an `allow` rule. See `Ash.Authorization.Rule.new/2` for more."
defmacro allow(check, opts \\ []) do defmacro allow(check, opts \\ []) do
quote bind_quoted: [check: check, opts: opts] do quote bind_quoted: [check: check, opts: opts] do
case Ash.Authorization.Rule.allow(check, opts) do case Ash.Authorization.Rule.allow(check, opts) do
@ -44,7 +44,7 @@ defmodule Ash.Resource.Actions do
end end
end end
@doc "Returns an `allow_unless` rule. See `Ash.Authorization.Rule.new/2 for more." @doc "Returns an `allow_unless` rule. See `Ash.Authorization.Rule.new/2` for more."
defmacro allow_unless(check, opts \\ []) do defmacro allow_unless(check, opts \\ []) do
quote bind_quoted: [check: check, opts: opts] do quote bind_quoted: [check: check, opts: opts] do
case Ash.Authorization.Rule.allow_unless(check, opts) do case Ash.Authorization.Rule.allow_unless(check, opts) do
@ -60,7 +60,7 @@ defmodule Ash.Resource.Actions do
end end
end end
@doc "Returns an `allow_only` rule. See `Ash.Authorization.Rule.new/2 for more." @doc "Returns an `allow_only` rule. See `Ash.Authorization.Rule.new/2` for more."
defmacro allow_only(check, opts \\ []) do defmacro allow_only(check, opts \\ []) do
quote bind_quoted: [check: check, opts: opts] do quote bind_quoted: [check: check, opts: opts] do
case Ash.Authorization.Rule.allow_only(check, opts) do case Ash.Authorization.Rule.allow_only(check, opts) do
@ -76,7 +76,7 @@ defmodule Ash.Resource.Actions do
end end
end end
@doc "Returns a `deny` rule. See `Ash.Authorization.Rule.new/2 for more." @doc "Returns a `deny` rule. See `Ash.Authorization.Rule.new/2` for more."
defmacro deny(check, opts \\ []) do defmacro deny(check, opts \\ []) do
quote bind_quoted: [check: check, opts: opts] do quote bind_quoted: [check: check, opts: opts] do
case Ash.Authorization.Rule.deny(check, opts) do case Ash.Authorization.Rule.deny(check, opts) do
@ -92,7 +92,7 @@ defmodule Ash.Resource.Actions do
end end
end end
@doc "Returns a `deny_unless` rule. See `Ash.Authorization.Rule.new/2 for more." @doc "Returns a `deny_unless` rule. See `Ash.Authorization.Rule.new/2` for more."
defmacro deny_unless(check, opts \\ []) do defmacro deny_unless(check, opts \\ []) do
quote bind_quoted: [check: check, opts: opts] do quote bind_quoted: [check: check, opts: opts] do
case Ash.Authorization.Rule.deny_unless(check, opts) do case Ash.Authorization.Rule.deny_unless(check, opts) do
@ -108,7 +108,7 @@ defmodule Ash.Resource.Actions do
end end
end end
@doc "Returns a `deny_only` rule. See `Ash.Authorization.Rule.new/2 for more." @doc "Returns a `deny_only` rule. See `Ash.Authorization.Rule.new/2` for more."
defmacro deny_only(check, opts \\ []) do defmacro deny_only(check, opts \\ []) do
quote bind_quoted: [check: check, opts: opts] do quote bind_quoted: [check: check, opts: opts] do
case Ash.Authorization.Rule.deny_only(check, opts) do case Ash.Authorization.Rule.deny_only(check, opts) do

View file

@ -1,19 +1,32 @@
defmodule Ash.Resource.Attributes.Attribute do defmodule Ash.Resource.Attributes.Attribute do
@doc false @doc false
defstruct [:name, :type, :primary_key?] defstruct [:name, :type, :primary_key?, :default]
@type t :: %__MODULE__{ @type t :: %__MODULE__{
name: atom(), name: atom(),
type: Ash.type(), type: Ash.type(),
primary_key?: boolean() primary_key?: boolean(),
default: (() -> term)
} }
@schema Ashton.schema( @schema Ashton.schema(
opts: [primary_key?: :boolean], opts: [
defaults: [primary_key?: false], primary_key?: :boolean,
default: [
{:function, 0},
{:tuple, {:module, :atom}},
{:tuple, {{:const, :constant}, :any}}
]
],
defaults: [
primary_key?: false
],
describe: [ describe: [
primary_key?: "Whether this field is, or is part of, the primary key of a resource." primary_key?:
"Whether this field is, or is part of, the primary key of a resource.",
default:
"A one argument function that returns a default value, an mfa that does the same, or a raw value via specifying `{:constant, value}`."
] ]
) )
@ -22,17 +35,37 @@ defmodule Ash.Resource.Attributes.Attribute do
@spec new(atom, Ash.Type.t(), Keyword.t()) :: {:ok, t()} | {:error, term} @spec new(atom, Ash.Type.t(), Keyword.t()) :: {:ok, t()} | {:error, term}
def new(name, type, opts) do def new(name, type, opts) do
case Ashton.validate(opts, @schema) do with {:ok, opts} <- Ashton.validate(opts, @schema),
{:ok, opts} -> {:default, {:ok, default}} <- {:default, cast_default(type, opts)} do
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
name: name, name: name,
type: type, type: type,
primary_key?: opts[:primary_key?] || false primary_key?: opts[:primary_key?],
default: default
}} }}
else
{:error, error} -> {:error, error}
{:default, _} -> {:error, [{:default, "is not a valid default for type #{inspect(type)}"}]}
end
end
{:error, error} -> defp cast_default(type, opts) do
{:error, error} case Keyword.fetch(opts, :default) do
{:ok, default} when is_function(default, 0) ->
{:ok, default}
{:ok, {mod, func}} when is_atom(mod) and is_atom(func) ->
{:ok, {mod, func}}
{:ok, {:constant, default}} ->
case Ash.Type.cast_input(type, default) do
{:ok, value} -> {:ok, {:constant, value}}
:error -> :error
end
:error ->
{:ok, nil}
end end
end end
end end

View file

@ -49,7 +49,7 @@ defmodule Ash.MixProject do
{:ecto, "~> 3.0"}, {:ecto, "~> 3.0"},
{:ets, "~> 0.8.0"}, {:ets, "~> 0.8.0"},
{:ex_doc, "~> 0.21", only: :dev, runtime: false}, {:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:ashton, "~> 0.3.10"} {:ashton, "~> 0.4.0"}
] ]
end end
end end

View file

@ -1,5 +1,5 @@
%{ %{
"ashton": {:hex, :ashton, "0.3.10", "ce0ab19f154c7fe8fefbc1486fdf7b601a0fa944555284182755197b1c073464", [:mix], [], "hexpm"}, "ashton": {:hex, :ashton, "0.4.0", "5d6fd833da9102d72a8b911498b55605282132450598af7e13b681a794067a4c", [:mix], [], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"db_connection": {:hex, :db_connection, "2.1.1", "a51e8a2ee54ef2ae6ec41a668c85787ed40cb8944928c191280fe34c15b76ae5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "db_connection": {:hex, :db_connection, "2.1.1", "a51e8a2ee54ef2ae6ec41a668c85787ed40cb8944928c191280fe34c15b76ae5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},

View file

@ -15,6 +15,11 @@ defmodule Ash.Test.Actions.CreateTest do
end end
end end
defmodule PostDefaults do
def garbage2(), do: "garbage2"
def garbage3(), do: "garbage3"
end
defmodule Post do defmodule Post do
use Ash.Resource, name: "posts", type: "post" use Ash.Resource, name: "posts", type: "post"
use Ash.DataLayer.Ets, private?: true use Ash.DataLayer.Ets, private?: true
@ -27,6 +32,9 @@ defmodule Ash.Test.Actions.CreateTest do
attributes do attributes do
attribute :title, :string attribute :title, :string
attribute :contents, :string attribute :contents, :string
attribute :tag, :string, default: {:constant, "garbage"}
attribute :tag2, :string, default: &PostDefaults.garbage2/0
attribute :tag3, :string, default: {PostDefaults, :garbage3}
end end
relationships do relationships do

View file

@ -49,7 +49,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if `interface?` is not a boolean" do test "it fails if `interface?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"`use Ash.Test.Resource.ApiTest.Api, ...` interface? must be of type :boolean", "`use Ash.Test.Resource.ApiTest.Api, ...` interface? must be boolean",
fn -> fn ->
defapi(interface?: 10) do defapi(interface?: 10) do
end end
@ -60,7 +60,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if `max_page_size` is not an integer" do test "it fails if `max_page_size` is not an integer" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"`use Ash.Test.Resource.ApiTest.Api, ...` max_page_size must be of type :integer", "`use Ash.Test.Resource.ApiTest.Api, ...` max_page_size must be integer",
fn -> fn ->
defapi(max_page_size: "ten") do defapi(max_page_size: "ten") do
end end
@ -82,7 +82,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if `default_page_size` is not an integer" do test "it fails if `default_page_size` is not an integer" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"`use Ash.Test.Resource.ApiTest.Api, ...` default_page_size must be of type :integer", "`use Ash.Test.Resource.ApiTest.Api, ...` default_page_size must be integer",
fn -> fn ->
defapi(default_page_size: "ten") do defapi(default_page_size: "ten") do
end end
@ -104,7 +104,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if the parallel_side_load supervisor is not an atom" do test "it fails if the parallel_side_load supervisor is not an atom" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"option supervisor at parallel_side_load must be of type :atom", "option supervisor at parallel_side_load must be atom",
fn -> fn ->
defapi do defapi do
parallel_side_load(supervisor: "foo") parallel_side_load(supervisor: "foo")
@ -116,7 +116,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if the max_concurrency is not an integer" do test "it fails if the max_concurrency is not an integer" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"option max_concurrency at parallel_side_load must be of type :integer", "option max_concurrency at parallel_side_load must be integer",
fn -> fn ->
defapi do defapi do
parallel_side_load(supervisor: :foo, max_concurrency: "foo") parallel_side_load(supervisor: :foo, max_concurrency: "foo")
@ -140,7 +140,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if the timeout is not an integer" do test "it fails if the timeout is not an integer" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"option timeout at parallel_side_load must be of type :integer", "option timeout at parallel_side_load must be integer",
fn -> fn ->
defapi do defapi do
parallel_side_load(supervisor: :foo, timeout: "foo") parallel_side_load(supervisor: :foo, timeout: "foo")
@ -164,7 +164,7 @@ defmodule Ash.Test.Resource.ApiTest do
test "it fails if the shutdown is not an integer" do test "it fails if the shutdown is not an integer" do
assert_raise( assert_raise(
Ash.Error.ApiDslError, Ash.Error.ApiDslError,
"option shutdown at parallel_side_load must be of type :integer", "option shutdown at parallel_side_load must be integer",
fn -> fn ->
defapi do defapi do
parallel_side_load(supervisor: :foo, shutdown: "foo") parallel_side_load(supervisor: :foo, shutdown: "foo")

View file

@ -48,7 +48,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.CreateTest do
test "it fails if `primary?` is not a boolean" do test "it fails if `primary?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option primary? at actions -> create -> default must be of type :boolean", "option primary? at actions -> create -> default must be boolean",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -62,7 +62,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.CreateTest do
test "it fails if `rules` is not a list" do test "it fails if `rules` is not a list" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> create -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> create -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -76,7 +76,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.CreateTest do
test "it fails if the elements of the rules list are not rules" do test "it fails if the elements of the rules list are not rules" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> create -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> create -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do

View file

@ -48,7 +48,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.DestroyTest do
test "it fails if `primary?` is not a boolean" do test "it fails if `primary?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option primary? at actions -> destroy -> default must be of type :boolean", "option primary? at actions -> destroy -> default must be boolean",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -62,7 +62,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.DestroyTest do
test "it fails if `rules` is not a list" do test "it fails if `rules` is not a list" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> destroy -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> destroy -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -76,7 +76,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.DestroyTest do
test "it fails if the elements of the rules list are not rules" do test "it fails if the elements of the rules list are not rules" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> destroy -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> destroy -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do

View file

@ -48,7 +48,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.ReadTest do
test "it fails if `primary?` is not a boolean" do test "it fails if `primary?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option primary? at actions -> read -> default must be of type :boolean", "option primary? at actions -> read -> default must be boolean",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -62,7 +62,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.ReadTest do
test "it fails if `rules` is not a list" do test "it fails if `rules` is not a list" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> read -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> read -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -76,7 +76,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.ReadTest do
test "it fails if the elements of the rules list are not rules" do test "it fails if the elements of the rules list are not rules" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> read -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> read -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do

View file

@ -48,7 +48,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.UpdateTest do
test "it fails if `primary?` is not a boolean" do test "it fails if `primary?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option primary? at actions -> update -> default must be of type :boolean", "option primary? at actions -> update -> default must be boolean",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -62,7 +62,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.UpdateTest do
test "it fails if `rules` is not a list" do test "it fails if `rules` is not a list" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> update -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> update -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do
@ -76,7 +76,7 @@ defmodule Ash.Test.Dsl.Resource.Actions.UpdateTest do
test "it fails if the elements of the rules list are not rules" do test "it fails if the elements of the rules list are not rules" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option rules at actions -> update -> default must be of type {:list, {:struct, Ash.Authorization.Rule}}", "option rules at actions -> update -> default must be [{:struct, Ash.Authorization.Rule}]",
fn -> fn ->
defposts do defposts do
actions do actions do

View file

@ -56,7 +56,7 @@ defmodule Ash.Test.Resource.AttributesTest do
test "raises if you pass an invalid value for `primary_key?`" do test "raises if you pass an invalid value for `primary_key?`" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option primary_key? at attributes -> attribute must be of type :boolean", "option primary_key? at attributes -> attribute must be boolean",
fn -> fn ->
defposts do defposts do
attributes do attributes do

View file

@ -55,7 +55,7 @@ defmodule Ash.Test.Resource.Relationships.BelongsToTest do
test "fails if destination_field is not an atom" do test "fails if destination_field is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option destination_field at relationships -> belongs_to -> foobar must be of type :atom", "option destination_field at relationships -> belongs_to -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -69,7 +69,7 @@ defmodule Ash.Test.Resource.Relationships.BelongsToTest do
test "fails if source_field is not an atom" do test "fails if source_field is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option source_field at relationships -> belongs_to -> foobar must be of type :atom", "option source_field at relationships -> belongs_to -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -111,7 +111,7 @@ defmodule Ash.Test.Resource.Relationships.BelongsToTest do
test "fails if `primary_key?` is not a boolean" do test "fails if `primary_key?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option primary_key? at relationships -> belongs_to -> foobar must be of type :boolean", "option primary_key? at relationships -> belongs_to -> foobar must be boolean",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -126,7 +126,7 @@ defmodule Ash.Test.Resource.Relationships.BelongsToTest do
test "fails if `define_field?` is not a boolean" do test "fails if `define_field?` is not a boolean" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option define_field? at relationships -> belongs_to -> foobar must be of type :boolean", "option define_field? at relationships -> belongs_to -> foobar must be boolean",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -140,7 +140,7 @@ defmodule Ash.Test.Resource.Relationships.BelongsToTest do
test "fails if `field_type` is not an atom" do test "fails if `field_type` is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option field_type at relationships -> belongs_to -> foobar must be of type :atom", "option field_type at relationships -> belongs_to -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do

View file

@ -36,7 +36,7 @@ defmodule Ash.Test.Resource.Relationshihps.HasManyTest do
test "fails if destination_field is not an atom" do test "fails if destination_field is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option destination_field at relationships -> has_many -> foobar must be of type :atom", "option destination_field at relationships -> has_many -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -50,7 +50,7 @@ defmodule Ash.Test.Resource.Relationshihps.HasManyTest do
test "fails if source_field is not an atom" do test "fails if source_field is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option source_field at relationships -> has_many -> foobar must be of type :atom", "option source_field at relationships -> has_many -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do

View file

@ -36,7 +36,7 @@ defmodule Ash.Test.Resource.Relationshihps.HasOneTest do
test "fails if destination_field is not an atom" do test "fails if destination_field is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option destination_field at relationships -> has_one -> foobar must be of type :atom", "option destination_field at relationships -> has_one -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -50,7 +50,7 @@ defmodule Ash.Test.Resource.Relationshihps.HasOneTest do
test "fails if source_field is not an atom" do test "fails if source_field is not an atom" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option source_field at relationships -> has_one -> foobar must be of type :atom", "option source_field at relationships -> has_one -> foobar must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do

View file

@ -55,7 +55,7 @@ defmodule Ash.Test.Resource.Relationships.ManyToManyTest do
test "it fails if you dont pass an atom for `source_field_on_join_table`" do test "it fails if you dont pass an atom for `source_field_on_join_table`" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option source_field_on_join_table at relationships -> many_to_many -> foobars must be of type :atom", "option source_field_on_join_table at relationships -> many_to_many -> foobars must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -69,7 +69,7 @@ defmodule Ash.Test.Resource.Relationships.ManyToManyTest do
test "it fails if you dont pass an atom for `destination_field_on_join_table`" do test "it fails if you dont pass an atom for `destination_field_on_join_table`" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option destination_field_on_join_table at relationships -> many_to_many -> foobars must be of type :atom", "option destination_field_on_join_table at relationships -> many_to_many -> foobars must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -85,7 +85,7 @@ defmodule Ash.Test.Resource.Relationships.ManyToManyTest do
test "it fails if you dont pass an atom for `source_field`" do test "it fails if you dont pass an atom for `source_field`" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option source_field at relationships -> many_to_many -> foobars must be of type :atom", "option source_field at relationships -> many_to_many -> foobars must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do
@ -101,7 +101,7 @@ defmodule Ash.Test.Resource.Relationships.ManyToManyTest do
test "it fails if you dont pass an atom for `destination_field`" do test "it fails if you dont pass an atom for `destination_field`" do
assert_raise( assert_raise(
Ash.Error.ResourceDslError, Ash.Error.ResourceDslError,
"option destination_field at relationships -> many_to_many -> foobars must be of type :atom", "option destination_field at relationships -> many_to_many -> foobars must be atom",
fn -> fn ->
defposts do defposts do
relationships do relationships do