feat: support cast_in_query?/0 and source

This commit is contained in:
Zach Daniel 2022-02-14 11:39:50 -05:00
parent e6ff1d8b4b
commit 97bd185c17
8 changed files with 88 additions and 41 deletions

View file

@ -18,7 +18,7 @@ jobs:
matrix: matrix:
otp: ["23"] otp: ["23"]
elixir: ["1.11.0"] elixir: ["1.11.0"]
ash: ["master", "1.50.20"] ash: ["master", "1.51.0"]
pg_version: ["9.6", "11"] pg_version: ["9.6", "11"]
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -229,14 +229,14 @@ defmodule AshPostgres.MigrationGenerator do
end end
defp add_order_to_operation(%{attribute: attribute} = op, attributes) do defp add_order_to_operation(%{attribute: attribute} = op, attributes) do
order = Enum.find_index(attributes, &(&1.name == attribute.name)) order = Enum.find_index(attributes, &(&1.source == attribute.source))
attribute = Map.put(attribute, :order, order) attribute = Map.put(attribute, :order, order)
%{op | attribute: attribute} %{op | attribute: attribute}
end end
defp add_order_to_operation(%{new_attribute: attribute} = op, attributes) do defp add_order_to_operation(%{new_attribute: attribute} = op, attributes) do
order = Enum.find_index(attributes, &(&1.name == attribute.name)) order = Enum.find_index(attributes, &(&1.source == attribute.source))
attribute = Map.put(attribute, :order, order) attribute = Map.put(attribute, :order, order)
%{op | new_attribute: attribute} %{op | new_attribute: attribute}
@ -330,7 +330,7 @@ defmodule AshPostgres.MigrationGenerator do
new_snapshot new_snapshot
| attributes: | attributes:
Enum.map(new_snapshot.attributes, fn attribute -> Enum.map(new_snapshot.attributes, fn attribute ->
if attribute.name in primary_key do if attribute.source in primary_key do
%{attribute | primary_key?: true} %{attribute | primary_key?: true}
else else
%{attribute | primary_key?: false} %{attribute | primary_key?: false}
@ -346,8 +346,8 @@ defmodule AshPostgres.MigrationGenerator do
attributes attributes
|> Enum.with_index() |> Enum.with_index()
|> Enum.map(fn {attr, i} -> Map.put(attr, :order, i) end) |> Enum.map(fn {attr, i} -> Map.put(attr, :order, i) end)
|> Enum.group_by(& &1.name) |> Enum.group_by(& &1.source)
|> Enum.map(fn {name, attributes} -> |> Enum.map(fn {source, attributes} ->
size = size =
attributes attributes
|> Enum.map(& &1.size) |> Enum.map(& &1.size)
@ -361,13 +361,13 @@ defmodule AshPostgres.MigrationGenerator do
end end
%{ %{
name: name, source: source,
type: merge_types(Enum.map(attributes, & &1.type), name, table), type: merge_types(Enum.map(attributes, & &1.type), source, table),
size: size, size: size,
default: merge_defaults(Enum.map(attributes, & &1.default)), default: merge_defaults(Enum.map(attributes, & &1.default)),
allow_nil?: Enum.any?(attributes, & &1.allow_nil?) || Enum.count(attributes) < count, allow_nil?: Enum.any?(attributes, & &1.allow_nil?) || Enum.count(attributes) < count,
generated?: Enum.any?(attributes, & &1.generated?), generated?: Enum.any?(attributes, & &1.generated?),
references: merge_references(Enum.map(attributes, & &1.references), name, table), references: merge_references(Enum.map(attributes, & &1.references), source, table),
primary_key?: false, primary_key?: false,
order: attributes |> Enum.map(& &1.order) |> Enum.min() order: attributes |> Enum.map(& &1.order) |> Enum.min()
} }
@ -534,7 +534,7 @@ defmodule AshPostgres.MigrationGenerator do
defp pkey_names(attributes) do defp pkey_names(attributes) do
attributes attributes
|> Enum.filter(& &1.primary_key?) |> Enum.filter(& &1.primary_key?)
|> Enum.map(& &1.name) |> Enum.map(& &1.source)
|> Enum.sort() |> Enum.sort()
end end
@ -1263,12 +1263,12 @@ defmodule AshPostgres.MigrationGenerator do
defp attribute_operations(snapshot, old_snapshot, opts) do defp attribute_operations(snapshot, old_snapshot, opts) do
attributes_to_add = attributes_to_add =
Enum.reject(snapshot.attributes, fn attribute -> Enum.reject(snapshot.attributes, fn attribute ->
Enum.find(old_snapshot.attributes, &(&1.name == attribute.name)) Enum.find(old_snapshot.attributes, &(&1.source == attribute.source))
end) end)
attributes_to_remove = attributes_to_remove =
Enum.reject(old_snapshot.attributes, fn attribute -> Enum.reject(old_snapshot.attributes, fn attribute ->
Enum.find(snapshot.attributes, &(&1.name == attribute.name)) Enum.find(snapshot.attributes, &(&1.source == attribute.source))
end) end)
{attributes_to_add, attributes_to_remove, attributes_to_rename} = {attributes_to_add, attributes_to_remove, attributes_to_rename} =
@ -1278,7 +1278,7 @@ defmodule AshPostgres.MigrationGenerator do
snapshot.attributes snapshot.attributes
|> Enum.map(fn attribute -> |> Enum.map(fn attribute ->
{attribute, {attribute,
Enum.find(old_snapshot.attributes, &(&1.name == attribute.name && &1 != attribute))} Enum.find(old_snapshot.attributes, &(&1.source == attribute.source && &1 != attribute))}
end) end)
|> Enum.filter(&elem(&1, 1)) |> Enum.filter(&elem(&1, 1))
@ -1439,7 +1439,7 @@ defmodule AshPostgres.MigrationGenerator do
defp resolve_renames(_table, [], removing, _opts), do: {[], removing, []} defp resolve_renames(_table, [], removing, _opts), do: {[], removing, []}
defp resolve_renames(table, [adding], [removing], opts) do defp resolve_renames(table, [adding], [removing], opts) do
if renaming_to?(table, removing.name, adding.name, opts) do if renaming_to?(table, removing.source, adding.source, opts) do
{[], [], [{adding, removing}]} {[], [], [{adding, removing}]}
else else
{[adding], [removing], []} {[adding], [removing], []}
@ -1476,9 +1476,9 @@ defmodule AshPostgres.MigrationGenerator do
defp renaming?(table, removing, opts) do defp renaming?(table, removing, opts) do
if opts.no_shell? do if opts.no_shell? do
raise "Unimplemented: cannot determine: Are you renaming #{table}.#{removing.name}? without shell input" raise "Unimplemented: cannot determine: Are you renaming #{table}.#{removing.source}? without shell input"
else else
Mix.shell().yes?("Are you renaming #{table}.#{removing.name}?") Mix.shell().yes?("Are you renaming #{table}.#{removing.source}?")
end end
end end
@ -1491,7 +1491,7 @@ defmodule AshPostgres.MigrationGenerator do
defp get_new_attribute(adding, tries) do defp get_new_attribute(adding, tries) do
name = name =
Mix.shell().prompt( Mix.shell().prompt(
"What are you renaming it to?: #{Enum.map_join(adding, ", ", & &1.name)}" "What are you renaming it to?: #{Enum.map_join(adding, ", ", & &1.source)}"
) )
name = name =
@ -1501,7 +1501,7 @@ defmodule AshPostgres.MigrationGenerator do
nil nil
end end
case Enum.find(adding, &(to_string(&1.name) == name)) do case Enum.find(adding, &(to_string(&1.source) == name)) do
nil -> get_new_attribute(adding, tries - 1) nil -> get_new_attribute(adding, tries - 1)
new_attribute -> new_attribute new_attribute -> new_attribute
end end
@ -1546,7 +1546,12 @@ defmodule AshPostgres.MigrationGenerator do
end) end)
|> Map.update!(:attributes, fn attributes -> |> Map.update!(:attributes, fn attributes ->
Enum.map(attributes, fn attribute -> Enum.map(attributes, fn attribute ->
if attribute.name == relationship.destination_field do destination_field_source =
relationship.destination
|> Ash.Resource.Info.attribute(relationship.destination_field)
|> Map.get(:source)
if attribute.source == destination_field_source do
source_attribute = source_attribute =
Ash.Resource.Info.attribute(relationship.source, relationship.source_field) Ash.Resource.Info.attribute(relationship.source, relationship.source_field)
@ -1657,7 +1662,9 @@ defmodule AshPostgres.MigrationGenerator do
resource resource
|> Ash.Resource.Info.attributes() |> Ash.Resource.Info.attributes()
|> Enum.map(&Map.take(&1, [:name, :type, :default, :allow_nil?, :generated?, :primary_key?])) |> Enum.map(
&Map.take(&1, [:name, :source, :type, :default, :allow_nil?, :generated?, :primary_key?])
)
|> Enum.map(fn attribute -> |> Enum.map(fn attribute ->
default = default(attribute, repo) default = default(attribute, repo)
@ -1687,6 +1694,7 @@ defmodule AshPostgres.MigrationGenerator do
|> Map.put(:default, default) |> Map.put(:default, default)
|> Map.put(:size, size) |> Map.put(:size, size)
|> Map.put(:type, type) |> Map.put(:type, type)
|> Map.delete(:name)
end) end)
|> Enum.map(fn attribute -> |> Enum.map(fn attribute ->
references = find_reference(resource, table, attribute) references = find_reference(resource, table, attribute)
@ -1697,9 +1705,15 @@ defmodule AshPostgres.MigrationGenerator do
defp find_reference(resource, table, attribute) do defp find_reference(resource, table, attribute) do
Enum.find_value(Ash.Resource.Info.relationships(resource), fn relationship -> Enum.find_value(Ash.Resource.Info.relationships(resource), fn relationship ->
if attribute.name == relationship.source_field && relationship.type == :belongs_to && source_field_name =
relationship.source
|> Ash.Resource.Info.attribute(relationship.source_field)
|> Map.get(:source)
if attribute.source == source_field_name && relationship.type == :belongs_to &&
foreign_key?(relationship) do foreign_key?(relationship) do
configured_reference = configured_reference(resource, table, attribute.name, relationship) configured_reference =
configured_reference(resource, table, attribute.source, relationship)
%{ %{
destination_field: relationship.destination_field, destination_field: relationship.destination_field,
@ -1917,8 +1931,14 @@ defmodule AshPostgres.MigrationGenerator do
{other, nil} {other, nil}
end end
attribute =
if Map.has_key?(attribute, :name) do
Map.put(attribute, :source, String.to_atom(attribute.name))
else
Map.update!(attribute, :source, &String.to_atom/1)
end
attribute attribute
|> Map.update!(:name, &String.to_atom/1)
|> Map.put(:type, type) |> Map.put(:type, type)
|> Map.put(:size, size) |> Map.put(:size, size)
|> Map.put_new(:default, "nil") |> Map.put_new(:default, "nil")
@ -1936,7 +1956,10 @@ defmodule AshPostgres.MigrationGenerator do
|> Map.put_new(:on_update, nil) |> Map.put_new(:on_update, nil)
|> Map.update!(:on_delete, &(&1 && String.to_atom(&1))) |> Map.update!(:on_delete, &(&1 && String.to_atom(&1)))
|> Map.update!(:on_update, &(&1 && String.to_atom(&1))) |> Map.update!(:on_update, &(&1 && String.to_atom(&1)))
|> Map.put(:name, Map.get(references, :name) || "#{table}_#{attribute.name}_fkey") |> Map.put(
:name,
Map.get(references, :name) || "#{table}_#{attribute.source}_fkey"
)
|> Map.put_new(:multitenancy, %{ |> Map.put_new(:multitenancy, %{
attribute: nil, attribute: nil,
strategy: nil, strategy: nil,

View file

@ -92,7 +92,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
"references(:#{table}", "references(:#{table}",
[ [
"column: #{inspect(destination_field)}", "column: #{inspect(destination_field)}",
@ -129,7 +129,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
"references(:#{table}", "references(:#{table}",
[ [
"column: #{inspect(destination_field)}", "column: #{inspect(destination_field)}",
@ -163,7 +163,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
inspect(attribute.type), inspect(attribute.type),
maybe_add_default(attribute.default), maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?), maybe_add_primary_key(attribute.primary_key?),
@ -191,7 +191,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
"references(:#{table}", "references(:#{table}",
[ [
"column: #{inspect(destination_field)}", "column: #{inspect(destination_field)}",
@ -227,7 +227,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
"references(:#{table}", "references(:#{table}",
[ [
"column: #{inspect(destination_field)}", "column: #{inspect(destination_field)}",
@ -257,7 +257,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
"references(:#{table}", "references(:#{table}",
[ [
"column: #{inspect(destination_field)}", "column: #{inspect(destination_field)}",
@ -277,7 +277,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
def up(%{attribute: %{type: :bigint, default: "nil", generated?: true} = attribute}) do def up(%{attribute: %{type: :bigint, default: "nil", generated?: true} = attribute}) do
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
":bigserial", ":bigserial",
maybe_add_null(attribute.allow_nil?), maybe_add_null(attribute.allow_nil?),
maybe_add_primary_key(attribute.primary_key?) maybe_add_primary_key(attribute.primary_key?)
@ -287,7 +287,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
def up(%{attribute: %{type: :integer, default: "nil", generated?: true} = attribute}) do def up(%{attribute: %{type: :integer, default: "nil", generated?: true} = attribute}) do
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
":serial", ":serial",
maybe_add_null(attribute.allow_nil?), maybe_add_null(attribute.allow_nil?),
maybe_add_primary_key(attribute.primary_key?) maybe_add_primary_key(attribute.primary_key?)
@ -302,7 +302,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
end end
[ [
"add #{inspect(attribute.name)}", "add #{inspect(attribute.source)}",
"#{inspect(attribute.type)}", "#{inspect(attribute.type)}",
maybe_add_null(attribute.allow_nil?), maybe_add_null(attribute.allow_nil?),
maybe_add_default(attribute.default), maybe_add_default(attribute.default),
@ -370,7 +370,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
inspect(attribute.type) inspect(attribute.type)
end end
"modify #{inspect(attribute.name)}, #{type_or_reference}#{alter_opts(attribute, old_attribute)}" "modify #{inspect(attribute.source)}, #{type_or_reference}#{alter_opts(attribute, old_attribute)}"
end end
defp reference( defp reference(
@ -533,11 +533,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
] ]
def up(%{old_attribute: old_attribute, new_attribute: new_attribute, table: table}) do def up(%{old_attribute: old_attribute, new_attribute: new_attribute, table: table}) do
"rename table(:#{table}), #{inspect(old_attribute.name)}, to: #{inspect(new_attribute.name)}" "rename table(:#{table}), #{inspect(old_attribute.source)}, to: #{inspect(new_attribute.source)}"
end end
def down(%{new_attribute: old_attribute, old_attribute: new_attribute, table: table}) do def down(%{new_attribute: old_attribute, old_attribute: new_attribute, table: table}) do
"rename table(:#{table}), #{inspect(old_attribute.name)}, to: #{inspect(new_attribute.name)}" "rename table(:#{table}), #{inspect(old_attribute.source)}, to: #{inspect(new_attribute.source)}"
end end
end end
@ -549,19 +549,19 @@ defmodule AshPostgres.MigrationGenerator.Operation do
""" """
# Attribute removal has been commented out to avoid data loss. See the migration generator documentation for more # Attribute removal has been commented out to avoid data loss. See the migration generator documentation for more
# If you uncomment this, be sure to also uncomment the corresponding attribute *addition* in the `down` migration # If you uncomment this, be sure to also uncomment the corresponding attribute *addition* in the `down` migration
# remove #{inspect(attribute.name)} # remove #{inspect(attribute.source)}
""" """
end end
def up(%{attribute: attribute}) do def up(%{attribute: attribute}) do
"remove #{inspect(attribute.name)}" "remove #{inspect(attribute.source)}"
end end
def down(%{attribute: attribute, multitenancy: multitenancy, commented?: true}) do def down(%{attribute: attribute, multitenancy: multitenancy, commented?: true}) do
prefix = """ prefix = """
# This is the `down` migration of the statement: # This is the `down` migration of the statement:
# #
# remove #{inspect(attribute.name)} # remove #{inspect(attribute.source)}
# #
""" """

View file

@ -13,7 +13,11 @@ defmodule AshPostgres.Types do
def parameterized_type(type, constraints) do def parameterized_type(type, constraints) do
if Ash.Type.ash_type?(type) do if Ash.Type.ash_type?(type) do
if Ash.Type.cast_in_query?(type) do
parameterized_type(Ash.Type.ecto_type(type), constraints) parameterized_type(Ash.Type.ecto_type(type), constraints)
else
:any
end
else else
if is_atom(type) && :erlang.function_exported(type, :type, 1) do if is_atom(type) && :erlang.function_exported(type, :type, 1) do
{:parameterized, type, constraints || []} {:parameterized, type, constraints || []}

View file

@ -97,7 +97,7 @@ defmodule AshPostgres.MixProject do
{:ecto, github: "elixir-ecto/ecto", branch: "master", override: true}, {:ecto, github: "elixir-ecto/ecto", branch: "master", override: true},
{:jason, "~> 1.0"}, {:jason, "~> 1.0"},
{:postgrex, ">= 0.0.0"}, {:postgrex, ">= 0.0.0"},
{:ash, ash_version("~> 1.50 and >= 1.50.20")}, {:ash, ash_version("~> 1.51 and >= 1.51.0")},
{:git_ops, "~> 2.4.5", only: :dev}, {:git_ops, "~> 2.4.5", only: :dev},
{:ex_doc, "~> 0.22", only: :dev, runtime: false}, {:ex_doc, "~> 0.22", only: :dev, runtime: false},
{:ex_check, "~> 0.11.0", only: :dev}, {:ex_check, "~> 0.11.0", only: :dev},

View file

@ -419,6 +419,17 @@ defmodule AshPostgres.FilterTest do
|> Ash.Query.filter(status_enum == ^"open") |> Ash.Query.filter(status_enum == ^"open")
|> Api.read_one!() |> Api.read_one!()
end end
test "it allows simple filtering without casting" do
Post
|> Ash.Changeset.new(status_enum_no_cast: "open")
|> Api.create!()
assert %{status_enum_no_cast: :open} =
Post
|> Ash.Query.filter(status_enum_no_cast == ^"open")
|> Api.read_one!()
end
end end
describe "atom filters" do describe "atom filters" do

View file

@ -53,6 +53,7 @@ defmodule AshPostgres.Test.Post do
attribute(:decimal, :decimal, default: Decimal.new(0)) attribute(:decimal, :decimal, default: Decimal.new(0))
attribute(:status, AshPostgres.Test.Types.Status) attribute(:status, AshPostgres.Test.Types.Status)
attribute(:status_enum, AshPostgres.Test.Types.StatusEnum) attribute(:status_enum, AshPostgres.Test.Types.StatusEnum)
attribute(:status_enum_no_cast, AshPostgres.Test.Types.StatusEnumNoCast, source: :status_enum)
create_timestamp(:created_at) create_timestamp(:created_at)
end end

View file

@ -0,0 +1,8 @@
defmodule AshPostgres.Test.Types.StatusEnumNoCast do
@moduledoc false
use Ash.Type.Enum, values: [:open, :closed]
def storage_type, do: :status
def cast_in_query?, do: false
end