diff --git a/lib/ash/resource.ex b/lib/ash/resource.ex index c279303d7..7e0a7360c 100644 --- a/lib/ash/resource.ex +++ b/lib/ash/resource.ex @@ -5,12 +5,6 @@ defmodule Ash.Resource do [Resource DSL documentation](dsl-ash-resource.html) """ - def foo do - # x + y + z - - 1 - 2 - 3 - end - @type t :: module @type record :: struct() @@ -39,6 +33,12 @@ defmodule Ash.Resource do doc: "Whether or not to validate that this resource is included in a domain.", default: true ], + primary_read_warning?: [ + type: :boolean, + doc: + "Set to `false` to silence warnings about arguments, preparations and filters on the primary read action.", + default: true + ], domain: [ type: {:behaviour, Ash.Domain}, doc: @@ -123,6 +123,7 @@ defmodule Ash.Resource do embed_nil_values?: opts[:embed_nil_values?] ] do @persist {:simple_notifiers, List.wrap(opts[:simple_notifiers])} + @persist {:primary_read_warning?, Keyword.get(opts, :primary_read_warning?, true)} if !(embedded? || has_domain?) do IO.warn(""" diff --git a/lib/ash/resource/dsl.ex b/lib/ash/resource/dsl.ex index 40c513497..c443b6bf2 100644 --- a/lib/ash/resource/dsl.ex +++ b/lib/ash/resource/dsl.ex @@ -1509,6 +1509,7 @@ defmodule Ash.Resource.Dsl do Ash.Resource.Verifiers.ValidateRelationshipAttributesMatch, Ash.Resource.Verifiers.VerifyReservedCalculationArguments, Ash.Resource.Verifiers.VerifyIdentityFields, + Ash.Resource.Verifiers.VerifyPrimaryReadActionHasNoArguments, Ash.Resource.Verifiers.VerifySelectedByDefault, Ash.Resource.Verifiers.EnsureAggregateFieldIsAttributeOrCalculation, Ash.Resource.Verifiers.ValidateRelationshipAttributes, diff --git a/lib/ash/resource/verifiers/verify_primary_read_action_has_no_arguments.ex b/lib/ash/resource/verifiers/verify_primary_read_action_has_no_arguments.ex new file mode 100644 index 000000000..b4eeb89d5 --- /dev/null +++ b/lib/ash/resource/verifiers/verify_primary_read_action_has_no_arguments.ex @@ -0,0 +1,53 @@ +defmodule Ash.Resource.Verifiers.VerifyPrimaryReadActionHasNoArguments do + @moduledoc "Verifies that primary read actions do not have any arguments" + use Spark.Dsl.Verifier + + def verify(dsl) do + resource = Spark.Dsl.Verifier.get_persisted(dsl, :module) + + if Spark.Dsl.Verifier.get_persisted(dsl, :primary_read_warning?) do + dsl + |> Ash.Resource.Info.actions() + |> Enum.find(&(&1.type == :read && &1.primary?)) + |> case do + nil -> + :ok + + action -> + if action.arguments != [] do + IO.warn(warning(resource, action, "arguments")) + end + + if action.filter not in [nil, []] do + IO.warn(warning(resource, action, "filters")) + end + + if action.preparations != [] && + Ash.Resource.Info.data_layer(resource) != Ash.DataLayer.Simple do + IO.warn(warning(resource, action, "preparations")) + end + + :ok + end + else + :ok + end + end + + defp warning(resource, action, things) do + """ + Primary read action `#{inspect(resource)}.#{action.name}` has #{things}. + + This is often done by mistake, but can also be done intentionally. + + Primary read actions are used when loading relationships, checking policies and more. + It is okay to have optional arguments, but required arguments are almost never desired. + + If you intended to have #{things} on your primary read action, add `primary_read_warning?: false` + to `use Ash.Resource`. For example: + + use Ash.Resource, + primary_read_warning?: false + """ + end +end