Skip to content

Commit e3291b8

Browse files
Behaviour return validation: validate callback return types across Ash behaviours
- Add Ash.BehaviourHelpers.call_and_validate_return for Notifier, Change, etc. - Notifier.notify/1 must return :ok - Change.atomic/3 validates return (e.g. {:not_atomic, String.t()}) - Fix test Notifiers to return :ok; fix test atomic changes to return {:not_atomic, reason} - Add behaviour_return_validation_test.exs Made-with: Cursor
1 parent a0c97e4 commit e3291b8

47 files changed

Lines changed: 548 additions & 236 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/ash/actions/create/bulk.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,7 +1959,7 @@ defmodule Ash.Actions.Create.Bulk do
19591959
end) ||
19601960
(module.has_batch_change?() &&
19611961
module.has_after_batch?() &&
1962-
module.batch_callbacks?(batch, change_opts, context))
1962+
Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context))
19631963

19641964
%{
19651965
state
@@ -2020,7 +2020,7 @@ defmodule Ash.Actions.Create.Bulk do
20202020
end) ||
20212021
(module.has_batch_change?() &&
20222022
module.has_after_batch?() &&
2023-
module.batch_callbacks?(batch, change_opts, context))
2023+
Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context))
20242024

20252025
%{
20262026
state

lib/ash/actions/destroy/bulk.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ defmodule Ash.Actions.Destroy.Bulk do
324324
fn
325325
%{change: {module, change_opts}} ->
326326
module.has_after_batch?() &&
327-
module.batch_callbacks?(query, change_opts, context)
327+
Ash.Resource.Change.batch_callbacks?(module, query, change_opts, context)
328328

329329
_ ->
330330
false

lib/ash/actions/helpers.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ defmodule Ash.Actions.Helpers do
8181
changeset
8282
)
8383

84-
if mod.batch_callbacks?([changeset], change_opts, context) do
84+
if Ash.Resource.Change.batch_callbacks?(mod, [changeset], change_opts, context) do
8585
[%{change | change: {mod, change_opts}}]
8686
else
8787
[]

lib/ash/actions/read/calculations.ex

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ defmodule Ash.Actions.Read.Calculations do
8080
source_context: opts[:context] || %{}
8181
}
8282

83-
if reuse_values? && module.has_expression?() do
83+
if reuse_values? && Ash.Resource.Calculation.has_expression?(module) do
8484
expr =
8585
case Ash.Resource.Calculation.expression(module, calc_opts, calc_context) do
8686
{:ok, result} -> {:ok, result}
@@ -167,7 +167,7 @@ defmodule Ash.Actions.Read.Calculations do
167167
"Failed to run calculation in memory, or in the data layer, and no `calculate/3` is defined on #{inspect(module)}. Data layer does not support one-off calculations."}
168168

169169
{:error, error} ->
170-
if module.has_calculate?() do
170+
if Ash.Resource.Calculation.has_calculate?(module) do
171171
case with_trace(
172172
fn ->
173173
Ash.Resource.Calculation.calculate(
@@ -236,7 +236,7 @@ defmodule Ash.Actions.Read.Calculations do
236236
{:error, error} -> {:error, error}
237237
end
238238
else
239-
if module.has_calculate?() do
239+
if Ash.Resource.Calculation.has_calculate?(module) do
240240
record =
241241
if primary_key do
242242
Ash.load(
@@ -880,7 +880,7 @@ defmodule Ash.Actions.Read.Calculations do
880880
{name, []} ->
881881
relationship = Ash.Resource.Info.relationship(ash_query.resource, name)
882882

883-
if calculation.module.strict_loads?() do
883+
if Ash.Resource.Calculation.strict_loads?(calculation.module) do
884884
[]
885885
else
886886
query = Ash.Query.new(relationship.destination)
@@ -991,7 +991,7 @@ defmodule Ash.Actions.Read.Calculations do
991991
|> Map.values()
992992
|> Enum.reduce_while({:ok, [], [], ash_query}, fn calculation,
993993
{:ok, in_query, at_runtime, ash_query} ->
994-
if calculation.module.has_expression?() do
994+
if Ash.Resource.Calculation.has_expression?(calculation.module) do
995995
expression =
996996
calculation.opts
997997
|> Ash.Expr.fill_template(
@@ -1043,7 +1043,7 @@ defmodule Ash.Actions.Read.Calculations do
10431043
{:halt, {:error, error}}
10441044
end
10451045
else
1046-
if calculation.module.has_calculate?() do
1046+
if Ash.Resource.Calculation.has_calculate?(calculation.module) do
10471047
{:cont, {:ok, in_query, [calculation | at_runtime], ash_query}}
10481048
else
10491049
{:cont,
@@ -1094,7 +1094,7 @@ defmodule Ash.Actions.Read.Calculations do
10941094
end
10951095
|> if do
10961096
if reuse_values? && calculation.module != Ash.Resource.Calculation.Expression &&
1097-
calculation.module.has_calculate?() do
1097+
Ash.Resource.Calculation.has_calculate?(calculation.module) do
10981098
{:ok, calculation}
10991099
else
11001100
Enum.reduce_while(initial_data, {:ok, []}, fn record, {:ok, results} ->
@@ -1163,7 +1163,7 @@ defmodule Ash.Actions.Read.Calculations do
11631163
end
11641164

11651165
defp should_be_in_expression?(calculation, expression \\ nil, ash_query) do
1166-
if calculation.module.has_expression?() do
1166+
if Ash.Resource.Calculation.has_expression?(calculation.module) do
11671167
case Map.fetch(calculation.context, :should_be_in_expression?) do
11681168
{:ok, value} ->
11691169
{:ok, value}
@@ -1199,7 +1199,7 @@ defmodule Ash.Actions.Read.Calculations do
11991199
expression
12001200
|> Ash.Filter.used_calculations(ash_query.resource, :*)
12011201
|> Enum.all?(fn %{module: module} ->
1202-
module.has_expression?()
1202+
Ash.Resource.Calculation.has_expression?(module)
12031203
end)
12041204
|> then(&{:ok, &1})
12051205

@@ -1230,7 +1230,7 @@ defmodule Ash.Actions.Read.Calculations do
12301230
if {calculation.module, calculation.opts} in checked_calculations do
12311231
query
12321232
else
1233-
has_expression? = calculation.module.has_expression?()
1233+
has_expression? = Ash.Resource.Calculation.has_expression?(calculation.module)
12341234

12351235
should_be_in_expression_result =
12361236
if has_expression? do
@@ -1277,7 +1277,7 @@ defmodule Ash.Actions.Read.Calculations do
12771277
calculation.name,
12781278
calculation.load,
12791279
calc_path,
1280-
calculation.module.strict_loads?(),
1280+
Ash.Resource.Calculation.strict_loads?(calculation.module),
12811281
relationship_path,
12821282
can_expression_calculation?,
12831283
checked_calculations,
@@ -2020,7 +2020,7 @@ defmodule Ash.Actions.Read.Calculations do
20202020

20212021
defp find_equivalent_calculation(query, calculation, authorize?) do
20222022
reusable? =
2023-
if authorize? && calculation.module.has_expression?() do
2023+
if authorize? && Ash.Resource.Calculation.has_expression?(calculation.module) do
20242024
Ash.Resource.Calculation.expression(
20252025
calculation.module,
20262026
calculation.opts,

lib/ash/actions/read/read.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,7 +2027,7 @@ defmodule Ash.Actions.Read do
20272027

20282028
case aggregate.field do
20292029
%Ash.Query.Calculation{} = calculation ->
2030-
if calculation.module.has_expression?() and
2030+
if Ash.Resource.Calculation.has_expression?(calculation.module) and
20312031
Ash.DataLayer.data_layer_can?(query.resource, :expression_calculation) do
20322032
expression =
20332033
Ash.Resource.Calculation.expression(
@@ -2476,7 +2476,7 @@ defmodule Ash.Actions.Read do
24762476
} = ref ->
24772477
calc = add_calc_context(calc, actor, authorize?, tenant, tracer, domain, resource, opts)
24782478

2479-
if Keyword.get(opts, :expand?, false) && calc.module.has_expression?() do
2479+
if Keyword.get(opts, :expand?, false) && Ash.Resource.Calculation.has_expression?(calc.module) do
24802480
expr =
24812481
case Ash.Resource.Calculation.expression(calc.module, calc.opts, calc.context) do
24822482
%Ash.Query.Function.Type{} = expr ->
@@ -2627,7 +2627,7 @@ defmodule Ash.Actions.Read do
26272627
defp should_expand_expression?(struct, calc, opts) do
26282628
struct == Ash.Query.Calculation &&
26292629
Keyword.get(opts, :expand?, true) &&
2630-
calc.module.has_expression?()
2630+
Ash.Resource.Calculation.has_expression?(calc.module)
26312631
end
26322632

26332633
defp expand_expression(calc, resource, parent_stack, first_combination) do
@@ -4594,7 +4594,7 @@ defmodule Ash.Actions.Read do
45944594

45954595
related_resource = Ash.Resource.Info.related(agg.resource, agg.relationship_path)
45964596

4597-
if calc.module.has_expression?() do
4597+
if Ash.Resource.Calculation.has_expression?(calc.module) do
45984598
expr =
45994599
case Ash.Resource.Calculation.expression(calc.module, calc.opts, calc.context) do
46004600
%Ash.Query.Function.Type{} = expr ->

lib/ash/actions/read/relationships.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1815,7 +1815,7 @@ defmodule Ash.Actions.Read.Relationships do
18151815
field: %Ash.Query.Calculation{module: module, opts: opts, context: context}
18161816
}
18171817
} ->
1818-
if module.has_expression?() do
1818+
if Ash.Resource.Calculation.has_expression?(module) do
18191819
Ash.Resource.Calculation.expression(module, opts, context)
18201820
|> do_has_parent_expr?(depth + 1)
18211821
else

lib/ash/actions/update/bulk.ex

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ defmodule Ash.Actions.Update.Bulk do
323323
fn
324324
%{change: {module, change_opts}} ->
325325
module.has_after_batch?() &&
326-
module.batch_callbacks?(query, change_opts, context)
326+
Ash.Resource.Change.batch_callbacks?(module,query, change_opts, context)
327327

328328
_ ->
329329
false
@@ -1157,7 +1157,7 @@ defmodule Ash.Actions.Update.Bulk do
11571157
|> Enum.filter(fn
11581158
{%{change: {module, change_opts}}, _index} ->
11591159
module.has_after_batch?() &&
1160-
module.batch_callbacks?(query, change_opts, context)
1160+
Ash.Resource.Change.batch_callbacks?(module,query, change_opts, context)
11611161

11621162
_ ->
11631163
false
@@ -2406,7 +2406,7 @@ defmodule Ash.Actions.Update.Bulk do
24062406
case change_opts do
24072407
{:templated, change_opts} ->
24082408
if module.has_before_batch?() && module.has_batch_change?() &&
2409-
module.batch_callbacks?(batch, change_opts, context) do
2409+
Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context) do
24102410
Ash.Resource.Change.before_batch(
24112411
module,
24122412
batch,
@@ -2420,7 +2420,7 @@ defmodule Ash.Actions.Update.Bulk do
24202420
change_opts ->
24212421
Enum.flat_map(batch, fn changeset ->
24222422
if module.has_before_batch?() && module.has_batch_change?() &&
2423-
module.batch_callbacks?(batch, change_opts, context) do
2423+
Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context) do
24242424
change_opts =
24252425
templated_opts(
24262426
change_opts,
@@ -2458,7 +2458,7 @@ defmodule Ash.Actions.Update.Bulk do
24582458
{:templated, change_opts} ->
24592459
if module.has_before_batch?() &&
24602460
module.has_batch_change?() &&
2461-
module.batch_callbacks?(matches, change_opts, context) do
2461+
Ash.Resource.Change.batch_callbacks?(module,matches, change_opts, context) do
24622462
Ash.Resource.Change.before_batch(
24632463
module,
24642464
matches,
@@ -2483,7 +2483,7 @@ defmodule Ash.Actions.Update.Bulk do
24832483

24842484
if module.has_before_batch?() &&
24852485
module.has_batch_change?() &&
2486-
module.batch_callbacks?([changeset], change_opts, context) do
2486+
Ash.Resource.Change.batch_callbacks?(module,[changeset], change_opts, context) do
24872487
Ash.Resource.Change.before_batch(
24882488
module,
24892489
[changeset],
@@ -3249,7 +3249,7 @@ defmodule Ash.Actions.Update.Bulk do
32493249
{:templated, change_opts} ->
32503250
if module.has_after_batch?() &&
32513251
module.has_batch_change?() &&
3252-
module.batch_callbacks?(changesets, change_opts, context) do
3252+
Ash.Resource.Change.batch_callbacks?(module,changesets, change_opts, context) do
32533253
Ash.Resource.Change.after_batch(
32543254
module,
32553255
results_for_callback,
@@ -3292,7 +3292,7 @@ defmodule Ash.Actions.Update.Bulk do
32923292

32933293
if module.has_after_batch?() &&
32943294
module.has_batch_change?() &&
3295-
module.batch_callbacks?(changesets, change_opts, context) do
3295+
Ash.Resource.Change.batch_callbacks?(module,changesets, change_opts, context) do
32963296
Ash.Resource.Change.after_batch(
32973297
module,
32983298
[{changeset, record}],
@@ -3340,7 +3340,7 @@ defmodule Ash.Actions.Update.Bulk do
33403340
{:templated, change_opts} ->
33413341
if module.has_after_batch?() &&
33423342
module.has_batch_change?() &&
3343-
module.batch_callbacks?(changesets, change_opts, context) do
3343+
Ash.Resource.Change.batch_callbacks?(module,changesets, change_opts, context) do
33443344
Ash.Resource.Change.after_batch(
33453345
module,
33463346
matches_for_callback,
@@ -3383,7 +3383,7 @@ defmodule Ash.Actions.Update.Bulk do
33833383
Enum.flat_map(matches_for_callback, fn {changeset, record} = result ->
33843384
if module.has_after_batch?() &&
33853385
module.has_batch_change?() &&
3386-
module.batch_callbacks?(changesets, change_opts, context) do
3386+
Ash.Resource.Change.batch_callbacks?(module,changesets, change_opts, context) do
33873387
change_opts =
33883388
templated_opts(
33893389
change_opts,
@@ -3597,7 +3597,7 @@ defmodule Ash.Actions.Update.Bulk do
35973597
end) ||
35983598
(module.has_after_batch?() &&
35993599
module.has_batch_change?() &&
3600-
module.batch_callbacks?(batch, change_opts, context))
3600+
Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context))
36013601

36023602
match_indices =
36033603
if Enum.empty?(non_matches) do
@@ -3729,7 +3729,7 @@ defmodule Ash.Actions.Update.Bulk do
37293729
{:templated, change_opts} ->
37303730
cond do
37313731
!must_be_atomic? && module.has_batch_change?() &&
3732-
module.batch_callbacks?(batch, change_opts, context) ->
3732+
Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context) ->
37333733
{:ok, change_opts} = Ash.Resource.Change.init(module, change_opts)
37343734
Ash.Resource.Change.batch_change(module, batch, change_opts, context)
37353735

@@ -3800,7 +3800,7 @@ defmodule Ash.Actions.Update.Bulk do
38003800
cond do
38013801
!must_be_atomic? && module.has_batch_change?() ->
38023802
Enum.flat_map(batch, fn changeset ->
3803-
if module.batch_callbacks?(batch, change_opts, context) do
3803+
if Ash.Resource.Change.batch_callbacks?(module,batch, change_opts, context) do
38043804
change_opts =
38053805
templated_opts(
38063806
change_opts,

lib/ash/authorizer.ex

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ defmodule Ash.Authorizer do
6060
Ash.Domain.t()
6161
) :: state
6262
def initial_state(module, actor, resource, action, domain) do
63-
result = module.initial_state(actor, resource, action, domain)
63+
result = apply(module, :initial_state, [actor, resource, action, domain])
6464

6565
if is_map(result) do
6666
result
@@ -78,7 +78,7 @@ defmodule Ash.Authorizer do
7878
@spec exception(module(), atom(), state()) :: no_return
7979
def exception(module, reason, state) do
8080
if function_exported?(module, :exception, 2) do
81-
module.exception(reason, state)
81+
apply(module, :exception, [reason, state])
8282
else
8383
if reason == :must_pass_strict_check do
8484
raise Ash.Error.Forbidden.MustPassStrictCheck.exception([])
@@ -91,7 +91,7 @@ defmodule Ash.Authorizer do
9191
@doc false
9292
@spec strict_check_context(module(), state()) :: [atom()]
9393
def strict_check_context(module, state) do
94-
result = module.strict_check_context(state)
94+
result = apply(module, :strict_check_context, [state])
9595

9696
if is_list(result) and Enum.all?(result, &is_atom/1) do
9797
Enum.uniq(result ++ [:query, :changeset])
@@ -190,18 +190,14 @@ defmodule Ash.Authorizer do
190190
{:ok, term()} | {:error, Ash.Error.t()}
191191
def alter_sort(module, state, sort, context) do
192192
if function_exported?(module, :alter_sort, 3) do
193-
result = module.alter_sort(sort, state, context)
194-
195-
if match?({:ok, _}, result) or match?({:error, _}, result) do
196-
result
197-
else
198-
raise Ash.Error.Framework.InvalidReturnType,
199-
message: """
200-
Invalid value returned from #{inspect(module)}.alter_sort/3.
201-
202-
The callback expects {:ok, sort} or {:error, Ash.Error.t()}.
203-
"""
204-
end
193+
Ash.BehaviourHelpers.call_and_validate_return(
194+
module,
195+
:alter_sort,
196+
[sort, state, context],
197+
[{:ok, :_}, {:error, :_}],
198+
behaviour: __MODULE__,
199+
callback_name: "alter_sort/3"
200+
)
205201
else
206202
{:ok, sort}
207203
end
@@ -210,7 +206,7 @@ defmodule Ash.Authorizer do
210206
@doc false
211207
@spec check_context(module(), state()) :: [atom()]
212208
def check_context(module, state) do
213-
result = module.check_context(state)
209+
result = apply(module, :check_context, [state])
214210

215211
if is_list(result) and Enum.all?(result, &is_atom/1) do
216212
result

0 commit comments

Comments
 (0)