Skip to content

Commit 83e8a71

Browse files
authored
refactor: Centralize multitenancy helper functions in Ash.Actions.Helpers (#2509)
* refactor: Centralize multitenancy helper functions in Ash.Actions.Helpers * fix: Remove redundant action nil guards in create/bulk.ex Adding a typespec to validate_bulk_multitenancy/3 caused dialyzer to infer that action is always a struct. This exposed redundant action && action.field guards that could never fail since action is validated as non-nil at the entry point (if !action do raise...).
1 parent 6e71d15 commit 83e8a71

File tree

7 files changed

+104
-135
lines changed

7 files changed

+104
-135
lines changed

lib/ash/actions/create/bulk.ex

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ defmodule Ash.Actions.Create.Bulk do
9999
opts
100100
)
101101

102-
case validate_multitenancy(resource, action, opts) do
102+
case Ash.Actions.Helpers.validate_bulk_multitenancy(resource, action, opts) do
103103
{:error, error} ->
104104
%Ash.BulkResult{
105105
status: :error,
@@ -398,7 +398,7 @@ defmodule Ash.Actions.Create.Bulk do
398398
defp base_changeset(resource, domain, opts, action) do
399399
upsert_condition =
400400
case opts[:upsert_condition] do
401-
nil -> action && action.upsert_condition
401+
nil -> action.upsert_condition
402402
other -> other
403403
end
404404

@@ -416,7 +416,7 @@ defmodule Ash.Actions.Create.Bulk do
416416
),
417417
upsert_condition: upsert_condition,
418418
return_skipped_upsert?:
419-
opts[:return_skipped_upsert?] || (action && action.return_skipped_upsert?) || false
419+
opts[:return_skipped_upsert?] || action.return_skipped_upsert? || false
420420
}
421421
})
422422
|> Ash.Actions.Helpers.add_context(opts)
@@ -777,21 +777,6 @@ defmodule Ash.Actions.Create.Bulk do
777777
end
778778
end
779779

780-
defp validate_multitenancy(resource, action, opts) do
781-
if Ash.Resource.Info.multitenancy_strategy(resource) &&
782-
!Ash.Resource.Info.multitenancy_global?(resource) && !opts[:tenant] &&
783-
Map.get(action, :multitenancy) not in [:bypass, :bypass_all, :allow_global] &&
784-
get_in(opts, [:context, :shared, :private, :multitenancy]) not in [
785-
:bypass,
786-
:bypass_all,
787-
:allow_global
788-
] do
789-
{:error, Ash.Error.Invalid.TenantRequired.exception(resource: resource)}
790-
else
791-
:ok
792-
end
793-
end
794-
795780
defp handle_params(changeset, false, action, opts, input, _argument_names) do
796781
Ash.Changeset.handle_params(changeset, action, input, opts)
797782
end
@@ -1282,12 +1267,12 @@ defmodule Ash.Actions.Create.Bulk do
12821267
),
12831268
upsert_condition:
12841269
case opts[:upsert_condition] do
1285-
nil -> action && action.upsert_condition
1270+
nil -> action.upsert_condition
12861271
other -> other
12871272
end,
12881273
return_skipped_upsert?:
12891274
case opts[:return_skipped_upsert?] do
1290-
nil -> action && action.return_skipped_upsert?
1275+
nil -> action.return_skipped_upsert?
12911276
other -> other
12921277
end,
12931278
tenant: Ash.ToTenant.to_tenant(opts[:tenant], resource)

lib/ash/actions/create/create.ex

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -615,10 +615,10 @@ defmodule Ash.Actions.Create do
615615
changeset
616616
end
617617

618-
if get_multitenancy_from_context(changeset) in [:bypass, :bypass_all, :allow_global] do
618+
if Helpers.get_multitenancy_from_context(changeset) in [:bypass, :bypass_all, :allow_global] do
619619
changeset
620620
else
621-
case validate_multitenancy(changeset) do
621+
case Helpers.validate_changeset_multitenancy(changeset) do
622622
:ok -> handle_attribute_multitenancy(changeset)
623623
{:error, error} -> Ash.Changeset.add_error(changeset, error)
624624
end
@@ -630,7 +630,7 @@ defmodule Ash.Actions.Create do
630630
:enforce ->
631631
changeset = handle_attribute_multitenancy(changeset)
632632

633-
case validate_multitenancy(changeset) do
633+
case Helpers.validate_changeset_multitenancy(changeset) do
634634
:ok -> changeset
635635
{:error, error} -> Ash.Changeset.add_error(changeset, error)
636636
end
@@ -662,24 +662,6 @@ defmodule Ash.Actions.Create do
662662
end
663663
end
664664

665-
defp validate_multitenancy(changeset) do
666-
if Ash.Resource.Info.multitenancy_strategy(changeset.resource) &&
667-
not Ash.Resource.Info.multitenancy_global?(changeset.resource) &&
668-
is_nil(changeset.tenant) do
669-
{:error, "#{inspect(changeset.resource)} changesets require a tenant to be specified"}
670-
else
671-
:ok
672-
end
673-
end
674-
675-
defp get_multitenancy_from_context(%{
676-
context: %{shared: %{private: %{multitenancy: multitenancy}}}
677-
}) do
678-
multitenancy
679-
end
680-
681-
defp get_multitenancy_from_context(_), do: nil
682-
683665
defp check_upsert_support(changeset, opts) do
684666
if opts[:upsert?] && !Ash.DataLayer.data_layer_can?(changeset.resource, :upsert) do
685667
Ash.Changeset.add_error(

lib/ash/actions/destroy/bulk.ex

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ defmodule Ash.Actions.Destroy.Bulk do
66
@moduledoc false
77
require Ash.Query
88

9+
alias Ash.Actions.Helpers
10+
911
@spec run(Ash.Domain.t(), Enumerable.t() | Ash.Query.t(), atom(), input :: map, Keyword.t()) ::
1012
Ash.BulkResult.t()
1113

@@ -75,7 +77,7 @@ defmodule Ash.Actions.Destroy.Bulk do
7577
if query.__validated_for_action__ do
7678
{query, opts}
7779
else
78-
{query, opts} = Ash.Actions.Helpers.set_context_and_get_opts(domain, query, opts)
80+
{query, opts} = Helpers.set_context_and_get_opts(domain, query, opts)
7981

8082
query =
8183
Ash.Query.for_read(
@@ -231,9 +233,9 @@ defmodule Ash.Actions.Destroy.Bulk do
231233

232234
atomic_changeset ->
233235
{atomic_changeset, opts} =
234-
Ash.Actions.Helpers.set_context_and_get_opts(domain, atomic_changeset, opts)
236+
Helpers.set_context_and_get_opts(domain, atomic_changeset, opts)
235237

236-
atomic_changeset = Ash.Actions.Helpers.apply_opts_load(atomic_changeset, opts)
238+
atomic_changeset = Helpers.apply_opts_load(atomic_changeset, opts)
237239

238240
atomic_changeset = %{atomic_changeset | domain: domain}
239241

@@ -321,7 +323,7 @@ defmodule Ash.Actions.Destroy.Bulk do
321323
if opts[:notify?] do
322324
remaining_notifications = Ash.Notifier.notify(notifications)
323325

324-
Ash.Actions.Helpers.warn_missed!(atomic_changeset.resource, action, %{
326+
Helpers.warn_missed!(atomic_changeset.resource, action, %{
325327
resource_notifications: remaining_notifications
326328
})
327329

@@ -446,7 +448,7 @@ defmodule Ash.Actions.Destroy.Bulk do
446448
List.wrap(Process.delete(:ash_notifications))
447449
)
448450

449-
Ash.Actions.Helpers.warn_missed!(resource, action, %{
451+
Helpers.warn_missed!(resource, action, %{
450452
resource_notifications: remaining_notifications
451453
})
452454
else
@@ -570,7 +572,12 @@ defmodule Ash.Actions.Destroy.Bulk do
570572
action_select
571573
)
572574

573-
with :ok <- validate_multitenancy(atomic_changeset.resource, atomic_changeset.action, opts),
575+
with :ok <-
576+
Helpers.validate_bulk_multitenancy(
577+
atomic_changeset.resource,
578+
atomic_changeset.action,
579+
opts
580+
),
574581
{:ok, query} <-
575582
authorize_bulk_query(query, atomic_changeset, opts),
576583
{:ok, atomic_changeset, query} <-
@@ -594,7 +601,7 @@ defmodule Ash.Actions.Destroy.Bulk do
594601
atomic_changeset,
595602
destroy_query_opts
596603
)
597-
|> Ash.Actions.Helpers.rollback_if_in_transaction(query.resource, nil) do
604+
|> Helpers.rollback_if_in_transaction(query.resource, nil) do
598605
:ok ->
599606
%Ash.BulkResult{
600607
status: :success
@@ -852,14 +859,14 @@ defmodule Ash.Actions.Destroy.Bulk do
852859

853860
defp do_run(domain, stream, action, input, opts, not_atomic_reason) do
854861
resource = opts[:resource]
855-
opts = Ash.Actions.Helpers.set_opts(opts, domain)
862+
opts = Helpers.set_opts(opts, domain)
856863

857864
read_action = Ash.Actions.Update.Bulk.get_read_action(resource, action, opts)
858865

859866
{context_cs, opts} =
860-
Ash.Actions.Helpers.set_context_and_get_opts(domain, Ash.Changeset.new(resource), opts)
867+
Helpers.set_context_and_get_opts(domain, Ash.Changeset.new(resource), opts)
861868

862-
case validate_multitenancy(resource, action, opts) do
869+
case Helpers.validate_bulk_multitenancy(resource, action, opts) do
863870
{:error, error} ->
864871
%Ash.BulkResult{
865872
status: :error,
@@ -970,21 +977,6 @@ defmodule Ash.Actions.Destroy.Bulk do
970977
end
971978
end
972979

973-
defp validate_multitenancy(resource, action, opts) do
974-
if Ash.Resource.Info.multitenancy_strategy(resource) &&
975-
!Ash.Resource.Info.multitenancy_global?(resource) && !opts[:tenant] &&
976-
Map.get(action, :multitenancy) not in [:bypass, :bypass_all, :allow_global] &&
977-
get_in(opts, [:context, :shared, :private, :multitenancy]) not in [
978-
:bypass,
979-
:bypass_all,
980-
:allow_global
981-
] do
982-
{:error, Ash.Error.Invalid.TenantRequired.exception(resource: resource)}
983-
else
984-
:ok
985-
end
986-
end
987-
988980
defp do_atomic_batches(atomic_changeset, domain, stream, action, input, opts, not_atomic_reason) do
989981
batch_size = opts[:batch_size] || 100
990982
resource = opts[:resource]
@@ -1258,7 +1250,7 @@ defmodule Ash.Actions.Destroy.Bulk do
12581250
|> Ash.Changeset.new()
12591251
|> Ash.Changeset.filter(opts[:filter])
12601252
|> Map.put(:domain, domain)
1261-
|> Ash.Actions.Helpers.add_context(opts)
1253+
|> Helpers.add_context(opts)
12621254
|> Ash.Changeset.set_context(opts[:context] || %{})
12631255
|> Ash.Changeset.prepare_changeset_for_action(action, opts)
12641256
|> Ash.Changeset.set_private_arguments_for_action(opts[:private_arguments] || %{})
@@ -1454,7 +1446,7 @@ defmodule Ash.Actions.Destroy.Bulk do
14541446
end
14551447

14561448
{batch, must_be_simple_results} =
1457-
Ash.Actions.Helpers.split_and_run_simple(
1449+
Helpers.split_and_run_simple(
14581450
batch,
14591451
action,
14601452
opts,
@@ -1583,7 +1575,7 @@ defmodule Ash.Actions.Destroy.Bulk do
15831575
remaining_notifications = Ash.Notifier.notify(notifications)
15841576
Process.delete(:ash_notifications) || []
15851577

1586-
Ash.Actions.Helpers.warn_missed!(resource, action, %{
1578+
Helpers.warn_missed!(resource, action, %{
15871579
resource_notifications: remaining_notifications
15881580
})
15891581

@@ -1948,7 +1940,7 @@ defmodule Ash.Actions.Destroy.Bulk do
19481940
Ash.Changeset.run_before_actions(changeset)
19491941

19501942
if !changeset.valid? && opts[:rollback_on_error?] do
1951-
Ash.Actions.Helpers.rollback_if_in_transaction(
1943+
Helpers.rollback_if_in_transaction(
19521944
{:error, changeset.errors},
19531945
resource,
19541946
nil
@@ -2050,7 +2042,7 @@ defmodule Ash.Actions.Destroy.Bulk do
20502042
)
20512043
]
20522044
end
2053-
|> Ash.Actions.Helpers.rollback_if_in_transaction(resource, nil)
2045+
|> Helpers.rollback_if_in_transaction(resource, nil)
20542046
|> Ash.Actions.BulkManualActionHelpers.process_bulk_results(
20552047
mod,
20562048
:bulk_destroy,
@@ -2066,7 +2058,7 @@ defmodule Ash.Actions.Destroy.Bulk do
20662058
result =
20672059
resource
20682060
|> Ash.DataLayer.destroy(changeset)
2069-
|> Ash.Actions.Helpers.rollback_if_in_transaction(resource, nil)
2061+
|> Helpers.rollback_if_in_transaction(resource, nil)
20702062

20712063
case result do
20722064
:ok ->
@@ -2136,7 +2128,7 @@ defmodule Ash.Actions.Destroy.Bulk do
21362128
) do
21372129
Enum.flat_map(batch_results, fn result ->
21382130
changeset =
2139-
Ash.Actions.Helpers.lookup_changeset(
2131+
Helpers.lookup_changeset(
21402132
result,
21412133
changesets_by_ref,
21422134
changesets_by_index,
@@ -2205,7 +2197,7 @@ defmodule Ash.Actions.Destroy.Bulk do
22052197
)
22062198
|> Enum.flat_map(fn result ->
22072199
changeset =
2208-
Ash.Actions.Helpers.lookup_changeset(
2200+
Helpers.lookup_changeset(
22092201
result,
22102202
changesets_by_ref,
22112203
changesets_by_index,
@@ -2280,14 +2272,14 @@ defmodule Ash.Actions.Destroy.Bulk do
22802272
authorize?: opts[:authorize?],
22812273
tracer: opts[:tracer]
22822274
)
2283-
|> Ash.Actions.Helpers.select(changeset)
2275+
|> Helpers.select(changeset)
22842276

22852277
other ->
22862278
other
22872279
end
22882280
end
22892281

22902282
defp notification(changeset, result, opts) do
2291-
Ash.Actions.Helpers.resource_notification(changeset, result, opts)
2283+
Helpers.resource_notification(changeset, result, opts)
22922284
end
22932285
end

lib/ash/actions/destroy/destroy.ex

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,10 @@ defmodule Ash.Actions.Destroy do
307307
_ -> changeset
308308
end
309309

310-
if get_multitenancy_from_context(changeset) in [:bypass, :bypass_all] do
310+
if Helpers.get_multitenancy_from_context(changeset) in [:bypass, :bypass_all] do
311311
changeset
312312
else
313-
case validate_multitenancy(changeset) do
313+
case Helpers.validate_changeset_multitenancy(changeset) do
314314
:ok -> handle_attribute_multitenancy(changeset)
315315
{:error, error} -> Ash.Changeset.add_error(changeset, error)
316316
end
@@ -322,7 +322,7 @@ defmodule Ash.Actions.Destroy do
322322
:enforce ->
323323
changeset = handle_attribute_multitenancy(changeset)
324324

325-
case validate_multitenancy(changeset) do
325+
case Helpers.validate_changeset_multitenancy(changeset) do
326326
:ok -> changeset
327327
{:error, error} -> Ash.Changeset.add_error(changeset, error)
328328
end
@@ -351,24 +351,6 @@ defmodule Ash.Actions.Destroy do
351351
end
352352
end
353353

354-
defp validate_multitenancy(changeset) do
355-
if Ash.Resource.Info.multitenancy_strategy(changeset.resource) &&
356-
not Ash.Resource.Info.multitenancy_global?(changeset.resource) &&
357-
is_nil(changeset.tenant) do
358-
{:error, "#{inspect(changeset.resource)} changesets require a tenant to be specified"}
359-
else
360-
:ok
361-
end
362-
end
363-
364-
defp get_multitenancy_from_context(%{
365-
context: %{shared: %{private: %{multitenancy: multitenancy}}}
366-
}) do
367-
multitenancy
368-
end
369-
370-
defp get_multitenancy_from_context(_), do: nil
371-
372354
defp validate_manual_action_return_result!({:ok, %resource{}} = result, resource, _) do
373355
result
374356
end

0 commit comments

Comments
 (0)