From efc60cd93241a5e404bb3e240d0a8cedace19646 Mon Sep 17 00:00:00 2001 From: Ryan Lyman Date: Thu, 14 May 2026 11:58:59 -0600 Subject: [PATCH 1/2] fix: handle removal of :context multitenancy in migrations --- .../migration_generator.ex | 94 ++++++++++++++----- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index 1177760a..e75a875b 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3109,32 +3109,22 @@ defmodule AshPostgres.MigrationGenerator do not is_nil(Map.get(attribute, :references)) and !(attribute.references.multitenancy && attribute.references.multitenancy.strategy == :context && - (is_nil(multitenancy) || multitenancy.strategy == :attribute)) + (is_nil(multitenancy) || multitenancy.strategy == :attribute)) and + !(multitenancy && multitenancy.strategy == :context && + attribute.references.multitenancy && + attribute.references.multitenancy.strategy == :context) end - def get_existing_snapshot(snapshot, opts) do - folder = get_snapshot_folder(snapshot, opts) - snapshot_path = get_snapshot_path(snapshot, folder) - - if File.exists?(snapshot_path) do - snapshot_path - |> File.ls!() - |> Enum.filter( - &(String.match?(&1, ~r/^\d{14}\.json$/) or - (opts.dev and String.match?(&1, ~r/^\d{14}\_dev.json$/))) - ) - |> case do - [] -> - get_old_snapshot(folder, snapshot) - - snapshot_files -> - snapshot_path - |> Path.join(Enum.max(snapshot_files)) - |> File.read!() - |> load_snapshot() - end + defp get_alternate_snapshot_folder(snapshot, opts) do + if snapshot.multitenancy.strategy == :context do + opts + |> snapshot_path(snapshot.repo) + |> Path.join(repo_name(snapshot.repo)) else - get_old_snapshot(folder, snapshot) + opts + |> snapshot_path(snapshot.repo) + |> Path.join(repo_name(snapshot.repo)) + |> Path.join("tenants") end end @@ -3151,6 +3141,64 @@ defmodule AshPostgres.MigrationGenerator do end end + def get_existing_snapshot(snapshot, opts) do + folder = get_snapshot_folder(snapshot, opts) + snapshot_path = get_snapshot_path(snapshot, folder) + + result = + if File.exists?(snapshot_path) do + snapshot_path + |> File.ls!() + |> Enum.filter( + &(String.match?(&1, ~r/^\d{14}\.json$/) or + (opts.dev and String.match?(&1, ~r/^\d{14}\_dev.json$/))) + ) + |> case do + [] -> + get_old_snapshot(folder, snapshot) + + snapshot_files -> + snapshot_path + |> Path.join(Enum.max(snapshot_files)) + |> File.read!() + |> load_snapshot() + end + else + get_old_snapshot(folder, snapshot) + end + + # If no snapshot was found in the primary folder, check the alternate folder. + # This handles the case where multitenancy strategy has changed (e.g. :context + # to nil/global), which causes the snapshot to be stored in a different folder. + if is_nil(result) do + alternate_folder = get_alternate_snapshot_folder(snapshot, opts) + alternate_path = get_snapshot_path(snapshot, alternate_folder) + + if File.exists?(alternate_path) do + alternate_path + |> File.ls!() + |> Enum.filter( + &(String.match?(&1, ~r/^\d{14}\.json$/) or + (opts.dev and String.match?(&1, ~r/^\d{14}\_dev.json$/))) + ) + |> case do + [] -> + get_old_snapshot(alternate_folder, snapshot) + + snapshot_files -> + alternate_path + |> Path.join(Enum.max(snapshot_files)) + |> File.read!() + |> load_snapshot() + end + else + get_old_snapshot(alternate_folder, snapshot) + end + else + result + end + end + defp get_snapshot_path(snapshot, folder) do if snapshot.schema do schema_dir = Path.join(folder, "#{snapshot.schema}.#{snapshot.table}") From e63751e26a76298ab0753633c3c020dc24991841 Mon Sep 17 00:00:00 2001 From: Ryan Lyman Date: Thu, 14 May 2026 13:34:22 -0600 Subject: [PATCH 2/2] fix: handle removal of :context multitenancy in migrations --- .../migration_generator.ex | 95 +++++++------------ 1 file changed, 36 insertions(+), 59 deletions(-) diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index e75a875b..84c07b47 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3115,85 +3115,62 @@ defmodule AshPostgres.MigrationGenerator do attribute.references.multitenancy.strategy == :context) end - defp get_alternate_snapshot_folder(snapshot, opts) do - if snapshot.multitenancy.strategy == :context do + defp get_snapshot_folder(snapshot, opts) do + base = opts |> snapshot_path(snapshot.repo) |> Path.join(repo_name(snapshot.repo)) + + if snapshot.multitenancy.strategy == :context do + Path.join(base, "tenants") else - opts - |> snapshot_path(snapshot.repo) - |> Path.join(repo_name(snapshot.repo)) - |> Path.join("tenants") + base end end - defp get_snapshot_folder(snapshot, opts) do - if snapshot.multitenancy.strategy == :context do + defp get_alternate_snapshot_folder(snapshot, opts) do + base = opts |> snapshot_path(snapshot.repo) |> Path.join(repo_name(snapshot.repo)) - |> Path.join("tenants") + + if snapshot.multitenancy.strategy == :context do + base else - opts - |> snapshot_path(snapshot.repo) - |> Path.join(repo_name(snapshot.repo)) + Path.join(base, "tenants") end end - def get_existing_snapshot(snapshot, opts) do - folder = get_snapshot_folder(snapshot, opts) + defp load_snapshot_from_folder(folder, snapshot, opts) do snapshot_path = get_snapshot_path(snapshot, folder) - result = - if File.exists?(snapshot_path) do - snapshot_path - |> File.ls!() - |> Enum.filter( - &(String.match?(&1, ~r/^\d{14}\.json$/) or - (opts.dev and String.match?(&1, ~r/^\d{14}\_dev.json$/))) - ) - |> case do - [] -> - get_old_snapshot(folder, snapshot) + if File.exists?(snapshot_path) do + snapshot_path + |> File.ls!() + |> Enum.filter( + &(String.match?(&1, ~r/^\d{14}\.json$/) or + (opts.dev and String.match?(&1, ~r/^\d{14}\_dev.json$/))) + ) + |> case do + [] -> + get_old_snapshot(folder, snapshot) - snapshot_files -> - snapshot_path - |> Path.join(Enum.max(snapshot_files)) - |> File.read!() - |> load_snapshot() - end - else - get_old_snapshot(folder, snapshot) + snapshot_files -> + snapshot_path + |> Path.join(Enum.max(snapshot_files)) + |> File.read!() + |> load_snapshot() end + else + get_old_snapshot(folder, snapshot) + end + end - # If no snapshot was found in the primary folder, check the alternate folder. - # This handles the case where multitenancy strategy has changed (e.g. :context - # to nil/global), which causes the snapshot to be stored in a different folder. - if is_nil(result) do - alternate_folder = get_alternate_snapshot_folder(snapshot, opts) - alternate_path = get_snapshot_path(snapshot, alternate_folder) - - if File.exists?(alternate_path) do - alternate_path - |> File.ls!() - |> Enum.filter( - &(String.match?(&1, ~r/^\d{14}\.json$/) or - (opts.dev and String.match?(&1, ~r/^\d{14}\_dev.json$/))) - ) - |> case do - [] -> - get_old_snapshot(alternate_folder, snapshot) + def get_existing_snapshot(snapshot, opts) do + result = load_snapshot_from_folder(get_snapshot_folder(snapshot, opts), snapshot, opts) - snapshot_files -> - alternate_path - |> Path.join(Enum.max(snapshot_files)) - |> File.read!() - |> load_snapshot() - end - else - get_old_snapshot(alternate_folder, snapshot) - end + if is_nil(result) do + load_snapshot_from_folder(get_alternate_snapshot_folder(snapshot, opts), snapshot, opts) else result end