improvement: add custom migration types, and repo level override

This commit is contained in:
Zach Daniel 2021-11-10 04:18:36 -05:00
parent ab89e0e4ae
commit af97c549c5
7 changed files with 165 additions and 10 deletions

View file

@ -14,6 +14,7 @@ locals_without_parens = [
index: 2,
message: 1,
migrate?: 1,
migration_types: 1,
name: 1,
on_delete: 1,
on_update: 1,

View file

@ -24,6 +24,11 @@ defmodule AshPostgres do
Extension.get_entities(resource, [:postgres, :references])
end
@doc "A keyword list of customized migration types"
def migration_types(resource) do
Extension.get_opt(resource, [:postgres], :migration_types, nil, true)
end
@doc "The configured check_constraints for a resource"
def check_constraints(resource) do
Extension.get_entities(resource, [:postgres, :check_constraints])

View file

@ -237,6 +237,12 @@ defmodule AshPostgres.DataLayer do
doc:
"Whether or not to include this resource in the generated migrations with `mix ash.generate_migrations`"
],
migration_types: [
type: :keyword_list,
default: [],
doc:
"A keyword list of attribute names to the ecto migration type that should be used for that attribute. Only necessary if you need to override the defaults."
],
base_filter_sql: [
type: :string,
doc:

View file

@ -344,9 +344,22 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn {attr, i} -> Map.put(attr, :order, i) end)
|> Enum.group_by(& &1.name)
|> Enum.map(fn {name, attributes} ->
size =
attributes
|> Enum.map(& &1.size)
|> Enum.filter(& &1)
|> case do
[] ->
nil
sizes ->
Enum.max(sizes)
end
%{
name: name,
type: merge_types(Enum.map(attributes, & &1.type), name, table),
size: size,
default: merge_defaults(Enum.map(attributes, & &1.default)),
allow_nil?: Enum.any?(attributes, & &1.allow_nil?) || Enum.count(attributes) < count,
generated?: Enum.any?(attributes, & &1.generated?),
@ -1643,11 +1656,32 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn attribute ->
default = default(attribute, repo)
type =
AshPostgres.migration_types(resource)[attribute.name] || migration_type(attribute.type)
type =
if :erlang.function_exported(repo, :override_migration_type, 1) do
repo.override_migration_type(type)
else
type
end
{type, size} =
case type do
{:varchar, size} ->
{:varchar, size}
{:binary, size} ->
{:binary, size}
other ->
{other, nil}
end
attribute
|> Map.put(:default, default)
|> Map.update!(:type, fn type ->
migration_type(type)
end)
|> Map.put(:size, size)
|> Map.put(:type, type)
end)
|> Enum.map(fn attribute ->
references = find_reference(resource, table, attribute)
@ -1788,17 +1822,25 @@ defmodule AshPostgres.MigrationGenerator do
snapshot
|> Map.update!(:attributes, fn attributes ->
Enum.map(attributes, fn attribute ->
%{attribute | type: sanitize_type(attribute.type)}
%{attribute | type: sanitize_type(attribute.type, attribute[:size])}
end)
end)
|> Jason.encode!(pretty: true)
end
defp sanitize_type({:array, type}) do
["array", sanitize_type(type)]
defp sanitize_type({:array, type}, size) do
["array", sanitize_type(type, size)]
end
defp sanitize_type(type) do
defp sanitize_type(:varchar, size) when not is_nil(size) do
["varchar", size]
end
defp sanitize_type(:binary, size) when not is_nil(size) do
["binary", size]
end
defp sanitize_type(type, _) do
type
end
@ -1856,9 +1898,24 @@ defmodule AshPostgres.MigrationGenerator do
end
defp load_attribute(attribute, table) do
type = load_type(attribute.type)
{type, size} =
case type do
{:varchar, size} ->
{:varchar, size}
{:binary, size} ->
{:binary, size}
other ->
{other, nil}
end
attribute
|> Map.update!(:type, &load_type/1)
|> Map.update!(:name, &String.to_atom/1)
|> Map.put(:type, type)
|> Map.put(:size, size)
|> Map.put_new(:default, "nil")
|> Map.update!(:default, &(&1 || "nil"))
|> Map.update!(:references, fn
@ -1897,6 +1954,14 @@ defmodule AshPostgres.MigrationGenerator do
{:array, load_type(type)}
end
defp load_type(["varchar", size]) do
{:varchar, size}
end
defp load_type(["binary", size]) do
{:binary, size}
end
defp load_type(type) do
String.to_atom(type)
end

View file

@ -86,6 +86,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"with: [#{source_attribute}: :#{destination_attribute}], match: :full"
end
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.name)}",
"references(:#{table}",
@ -95,7 +100,8 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
on_delete(reference),
on_update(reference)
on_update(reference),
size
],
")",
maybe_add_default(attribute.default),
@ -117,6 +123,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
} = reference
} = attribute
}) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.name)}",
"references(:#{table}",
@ -125,6 +136,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"prefix: \"public\"",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
on_delete(reference),
on_update(reference)
],
@ -145,11 +157,17 @@ defmodule AshPostgres.MigrationGenerator.Operation do
}
} = attribute
}) do
size =
if attribute.size do
"size: #{attribute.size}"
end
[
"add #{inspect(attribute.name)}",
inspect(attribute.type),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
size,
maybe_add_null(attribute.allow_nil?)
]
|> join()
@ -167,6 +185,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
} = reference
} = attribute
}) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.name)}",
"references(:#{table}",
@ -175,6 +198,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
"prefix: prefix()",
size,
on_delete(reference),
on_update(reference)
],
@ -197,6 +221,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
} = reference
} = attribute
}) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.name)}",
"references(:#{table}",
@ -205,6 +234,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
"prefix: \"public\"",
size,
on_delete(reference),
on_update(reference)
],
@ -221,6 +251,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
%{references: %{table: table, destination_field: destination_field} = reference} =
attribute
}) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.name)}",
"references(:#{table}",
@ -228,6 +263,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"column: #{inspect(destination_field)}",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
on_delete(reference),
on_update(reference)
],
@ -260,11 +296,17 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end
def up(%{attribute: attribute}) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.name)}",
"#{inspect(attribute.type)}",
maybe_add_null(attribute.allow_nil?),
maybe_add_default(attribute.default),
size,
maybe_add_primary_key(attribute.primary_key?)
]
|> join()
@ -342,10 +384,16 @@ defmodule AshPostgres.MigrationGenerator.Operation do
} = reference
} = attribute
) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
join([
"references(:#{table}, column: #{inspect(destination_field)}",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
"prefix: prefix()",
on_delete(reference),
on_update(reference),
@ -369,11 +417,17 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"with: [#{source_attribute}: :#{destination_attribute}], match: :full"
end
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
join([
"references(:#{table}, column: #{inspect(destination_field)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
on_delete(reference),
on_update(reference),
")"
@ -390,10 +444,16 @@ defmodule AshPostgres.MigrationGenerator.Operation do
} = reference
} = attribute
) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
join([
"references(:#{table}, column: #{inspect(destination_field)}, prefix: \"public\"",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
on_delete(reference),
on_update(reference),
")"
@ -410,10 +470,16 @@ defmodule AshPostgres.MigrationGenerator.Operation do
} = reference
} = attribute
) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
join([
"references(:#{table}, column: #{inspect(destination_field)}",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
on_delete(reference),
on_update(reference),
")"

View file

@ -35,6 +35,8 @@ defmodule AshPostgres.Repo do
@callback migrations_path() :: String.t()
@doc "The default prefix(postgres schema) to use when building queries"
@callback default_prefix() :: String.t()
@doc "Allows overriding a given migration type for *all* fields, for example if you wanted to always use :timestamptz for :utc_datetime fields"
@callback override_migration_type(atom) :: atom
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@ -48,6 +50,7 @@ defmodule AshPostgres.Repo do
def tenant_migrations_path, do: nil
def migrations_path, do: nil
def default_prefix, do: "public"
def override_migration_type(type), do: type
def all_tenants do
raise """
@ -77,7 +80,8 @@ defmodule AshPostgres.Repo do
installed_extensions: 0,
all_tenants: 0,
tenant_migrations_path: 0,
default_prefix: 0
default_prefix: 0,
override_migration_type: 1
end
end
end

View file

@ -67,6 +67,10 @@ defmodule AshPostgres.MigrationGeneratorTest do
end)
defposts do
postgres do
migration_types second_title: {:varchar, 16}
end
identities do
identity(:title, [:title])
end
@ -74,6 +78,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
attributes do
uuid_primary_key(:id)
attribute(:title, :string)
attribute(:second_title, :string)
end
end
@ -116,6 +121,9 @@ defmodule AshPostgres.MigrationGeneratorTest do
# the migration adds other attributes
assert file_contents =~ ~S[add :title, :text]
# the migration adds custom attributes
assert file_contents =~ ~S[add :second_title, :varchar, size: 16]
# the migration creates unique_indexes based on the identities of the resource
assert file_contents =~ ~S{create unique_index(:posts, [:title], name: "posts_title_index")}
end