Skip to content

Commit 7426b74

Browse files
committed
chore: return InvalidLoad when loading field?: false calcs
1 parent 3246d93 commit 7426b74

File tree

3 files changed

+78
-18
lines changed

3 files changed

+78
-18
lines changed

lib/ash/query/query.ex

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,7 +2216,9 @@ defmodule Ash.Query do
22162216
end
22172217

22182218
{field, {args, load_through}}, query ->
2219-
if resource_calculation = Ash.Resource.Info.calculation(query.resource, field) do
2219+
resource_calculation = Ash.Resource.Info.calculation(query.resource, field)
2220+
2221+
if Ash.Resource.Calculation.can_load?(resource_calculation) do
22202222
load_resource_calculation(query, resource_calculation, args, load_through)
22212223
else
22222224
add_error(
@@ -2232,7 +2234,15 @@ defmodule Ash.Query do
22322234
load_relationship(query, rel, rest, opts)
22332235

22342236
resource_calculation = Ash.Resource.Info.calculation(query.resource, field) ->
2235-
load_resource_calculation(query, resource_calculation, rest)
2237+
if Ash.Resource.Calculation.can_load?(resource_calculation) do
2238+
load_resource_calculation(query, resource_calculation, rest)
2239+
else
2240+
add_error(
2241+
query,
2242+
:load,
2243+
Ash.Error.Query.InvalidLoad.exception(load: [{field, rest}])
2244+
)
2245+
end
22362246

22372247
attribute = Ash.Resource.Info.attribute(query.resource, field) ->
22382248
if Ash.Type.can_load?(attribute.type, attribute.constraints) do
@@ -2360,11 +2370,12 @@ defmodule Ash.Query do
23602370
end
23612371
end
23622372

2373+
# TODO: Remove this dead function - resource_calc_to_calc/4
23632374
@doc false
2364-
def resource_calc_to_calc(query, name, resource_calculation, args \\ %{}) do
2375+
def resource_calc_to_calc(query, _name, resource_calculation, args \\ %{}) do
23652376
{name, load} =
23662377
case fetch_key(args, :as) do
2367-
:error -> {name, name}
2378+
:error -> Ash.Resource.Calculation.query_name_and_load(resource_calculation)
23682379
{:ok, key} -> {key, nil}
23692380
end
23702381

@@ -2475,7 +2486,15 @@ defmodule Ash.Query do
24752486
end
24762487

24772488
resource_calculation = Ash.Resource.Info.calculation(query.resource, field) ->
2478-
load_resource_calculation(query, resource_calculation, %{})
2489+
if Ash.Resource.Calculation.can_load?(resource_calculation) do
2490+
load_resource_calculation(query, resource_calculation, %{})
2491+
else
2492+
add_error(
2493+
query,
2494+
:load,
2495+
Ash.Error.Query.InvalidLoad.exception(load: field)
2496+
)
2497+
end
24792498

24802499
true ->
24812500
add_error(query, :load, Ash.Error.Query.InvalidLoad.exception(load: field))
@@ -3635,14 +3654,17 @@ defmodule Ash.Query do
36353654

36363655
args = Map.put(args, :as, as_name)
36373656

3638-
if resource_calculation = Ash.Resource.Info.calculation(query.resource, calc_name) do
3657+
with %Ash.Resource.Calculation{} = resource_calculation <-
3658+
Ash.Resource.Info.calculation(query.resource, calc_name),
3659+
true <- Ash.Resource.Calculation.can_load?(resource_calculation) do
36393660
if opts[:load_through] do
36403661
load_resource_calculation(query, resource_calculation, args)
36413662
else
36423663
load_resource_calculation(query, resource_calculation, args, opts[:load_through])
36433664
end
36443665
else
3645-
add_error(query, "No such calculation: #{inspect(calc_name)}")
3666+
nil -> add_error(query, "No such calculation: #{inspect(calc_name)}")
3667+
false -> add_error(query, :load, Ash.Error.Query.InvalidLoad.exception(load: calc_name))
36463668
end
36473669
end
36483670

lib/ash/resource/calculation/calculation.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ defmodule Ash.Resource.Calculation do
219219
{:ok, {Ash.Resource.Calculation.Expression, expr: expr}}
220220
end
221221

222+
@doc false
223+
@spec can_load?(t() | nil) :: boolean()
224+
def can_load?(%__MODULE__{} = calculation), do: calculation.field?
225+
def can_load?(_), do: false
226+
222227
@doc false
223228
@spec query_name_and_load(t()) :: {atom, atom | nil}
224229
def query_name_and_load(calculation) do

test/resource/calculations_test.exs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ defmodule Ash.Test.Resource.CalculationsTest do
104104
refute Map.has_key?(post, :non_field_calculation)
105105
end
106106

107-
test "Calculation with field?: false cannot be loaded directly, but can be used in expressions" do
108-
defmodule Post do
107+
test "Calculation with field?: false can be used in expressions" do
108+
defmodule PostForCalculation do
109109
@moduledoc false
110110
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
111111

@@ -137,19 +137,52 @@ defmodule Ash.Test.Resource.CalculationsTest do
137137
end
138138
end
139139

140-
Post
141-
|> Ash.Changeset.for_create(:create, %{name: "name", contents: "contents"})
142-
|> Ash.create!()
143-
144140
post =
145-
Post
146-
|> Ash.Query.for_read(:read, %{})
147-
|> Ash.read_one!()
148-
|> Ash.load!([:non_field_calculation, :name_and_contents])
141+
PostForCalculation
142+
|> Ash.Changeset.for_create(:create, %{name: "name", contents: "contents"})
143+
|> Ash.create!()
144+
|> Ash.load!([:name_and_contents])
149145

150146
assert :name_and_contents in Map.keys(post)
151147
assert post.name_and_contents == "namecontents"
152-
refute :non_field_calculation in Map.keys(post)
148+
end
149+
150+
test "Calculation with field?: false returns InvalidLoad error when loaded directly" do
151+
defmodule PostForCalculationError do
152+
@moduledoc false
153+
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
154+
155+
attributes do
156+
uuid_primary_key :id
157+
158+
attribute :name, :string do
159+
public?(true)
160+
end
161+
162+
attribute :contents, :string do
163+
public?(true)
164+
end
165+
end
166+
167+
actions do
168+
default_accept :*
169+
defaults [:read, :destroy, update: :*, create: :*]
170+
end
171+
172+
calculations do
173+
calculate :non_field_calculation, :string, concat([:name, :contents]) do
174+
field?(false)
175+
end
176+
end
177+
end
178+
179+
post =
180+
PostForCalculationError
181+
|> Ash.Changeset.for_create(:create, %{name: "name", contents: "contents"})
182+
|> Ash.create!()
183+
184+
assert {:error, %{errors: [%Ash.Error.Query.InvalidLoad{load: :non_field_calculation}]}} =
185+
Ash.load(post, [:non_field_calculation])
153186
end
154187

155188
test "Calculation descriptions are allowed" do

0 commit comments

Comments
 (0)