feat: support base_filter (#18)

This commit is contained in:
Zach Daniel 2020-09-19 18:08:09 -04:00 committed by GitHub
parent 4299cbaa05
commit 51cb9c1a68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 331 additions and 178 deletions

View file

@ -1,6 +1,12 @@
# THIS FILE IS AUTOGENERATED USING `mix ash.formatter`
# DONT MODIFY IT BY HAND
locals_without_parens = [migrate?: 1, repo: 1, table: 1]
locals_without_parens = [
base_filter_sql: 1,
migrate?: 1,
repo: 1,
skip_unique_indexes: 1,
table: 1
]
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],

View file

@ -9,12 +9,12 @@ defmodule AshPostgres do
alias Ash.Dsl.Extension
@doc "Fetch the configured repo for a resource"
@doc "The configured repo for a resource"
def repo(resource) do
Extension.get_opt(resource, [:postgres], :repo, nil, true)
end
@doc "Fetch the configured table for a resource"
@doc "The configured table for a resource"
def table(resource) do
Extension.get_opt(resource, [:postgres], :table, nil, true)
end
@ -23,4 +23,14 @@ defmodule AshPostgres do
def migrate?(resource) do
Extension.get_opt(resource, [:postgres], :migrate?, nil, true)
end
@doc "A stringified version of the base_filter, to be used in a where clause when generating unique indexes"
def base_filter_sql(resource) do
Extension.get_opt(resource, [:postgres], :base_filter_sql, nil)
end
@doc "Skip generating unique indexes when generating migrations"
def skip_unique_indexes?(resource) do
Extension.get_opt(resource, [:postgres], :skip_unique_indexes?, [])
end
end

View file

@ -47,6 +47,16 @@ defmodule AshPostgres.DataLayer do
doc:
"Whether or not to include this resource in the generated migrations with `mix ash.generate_migrations`"
],
base_filter_sql: [
type: :string,
doc:
"A raw sql version of the base_filter, e.g `representative = true`. Required if trying to create a unique constraint on a resource with a base_filter"
],
skip_unique_indexes: [
type: {:custom, __MODULE__, :validate_skip_unique_indexes, []},
default: false,
doc: "Skip generating unique indexes when generating migrations"
],
table: [
type: :string,
required: true,
@ -75,6 +85,17 @@ defmodule AshPostgres.DataLayer do
end
end
@doc false
def validate_skip_unique_indexes(indexes) do
indexes = List.wrap(indexes)
if Enum.all?(indexes, &is_atom/1) do
{:ok, indexes}
else
{:error, "All indexes to skip must be atoms"}
end
end
@impl true
def custom_filters(resource) do
config = repo(resource).config()
@ -154,6 +175,10 @@ defmodule AshPostgres.DataLayer do
@impl true
def offset(query, nil, _), do: query
def offset(%{offset: old_offset} = query, 0, _resource) when old_offset in [0, nil] do
{:ok, query}
end
def offset(query, offset, _resource) do
{:ok, from(row in query, offset: ^offset)}
end
@ -183,8 +208,10 @@ defmodule AshPostgres.DataLayer do
)
)
data_layer_query = Ash.Query.new(source_resource).data_layer_query
query =
from(source in resource_to_query(source_resource),
from(source in data_layer_query,
as: :source_record,
where: field(source, ^source_field) in ^source_values,
inner_lateral_join: destination in ^subquery,
@ -793,15 +820,20 @@ defmodule AshPostgres.DataLayer do
end
defp add_filter_expression(query, filter) do
filter
|> split_and_statements()
|> Enum.reduce(query, fn filter, query ->
clause = filter_to_dynamic_expr(filter, query.__ash_bindings__.bindings)
wheres =
filter
|> split_and_statements()
|> Enum.map(fn filter ->
{params, expr} = filter_to_expr(filter, query.__ash_bindings__.bindings, [])
from(row in query,
where: ^clause
)
end)
%Ecto.Query.BooleanExpr{
expr: expr,
op: :and,
params: params
}
end)
%{query | wheres: query.wheres ++ wheres}
end
defp split_and_statements(%Filter{expression: expression}) do
@ -826,113 +858,6 @@ defmodule AshPostgres.DataLayer do
defp split_and_statements(other), do: [other]
defp filter_to_dynamic_expr(%Filter{expression: expression}, bindings) do
filter_to_dynamic_expr(expression, bindings)
end
defp filter_to_dynamic_expr(nil, _), do: true
defp filter_to_dynamic_expr(true, _), do: true
defp filter_to_dynamic_expr(false, _), do: false
defp filter_to_dynamic_expr(%Expression{op: :and, left: left, right: right}, bindings) do
left = filter_to_dynamic_expr(left, bindings)
right = filter_to_dynamic_expr(right, bindings)
Ecto.Query.dynamic([row], ^left and ^right)
end
defp filter_to_dynamic_expr(%Expression{op: :or, left: left, right: right}, bindings) do
left = filter_to_dynamic_expr(left, bindings)
right = filter_to_dynamic_expr(right, bindings)
Ecto.Query.dynamic([row], ^left or ^right)
end
defp filter_to_dynamic_expr(%Not{expression: expression}, bindings) do
expression = filter_to_dynamic_expr(expression, bindings)
Ecto.Query.dynamic([row], not (^expression))
end
defp filter_to_dynamic_expr(%Predicate{} = pred, bindings) do
%{predicate: predicate, relationship_path: relationship_path, attribute: attribute} = pred
current_binding =
case attribute do
%Ash.Resource.Attribute{} ->
Enum.find_value(bindings, fn {binding, data} ->
data.path == relationship_path && data.type in [:left, :inner, :root] && binding
end)
%Ash.Query.Aggregate{} = aggregate ->
Enum.find_value(bindings, fn {binding, data} ->
data.path == aggregate.relationship_path && data.type == :aggregate && binding
end)
end
type = Ash.Type.ecto_type(attribute.type)
filter_value_to_dynamic_expr(attribute, predicate, type, current_binding)
end
defp filter_value_to_dynamic_expr(attribute, %Eq{value: value}, _type, current_binding) do
Ecto.Query.dynamic([{row, current_binding}], field(row, ^attribute.name) == ^value)
end
defp filter_value_to_dynamic_expr(attribute, %LessThan{value: value}, _type, current_binding) do
Ecto.Query.dynamic([{row, current_binding}], field(row, ^attribute.name) < ^value)
end
defp filter_value_to_dynamic_expr(attribute, %GreaterThan{value: value}, _type, current_binding) do
Ecto.Query.dynamic([{row, current_binding}], field(row, ^attribute.name) > ^value)
end
defp filter_value_to_dynamic_expr(attribute, %In{values: values}, _type, current_binding) do
Ecto.Query.dynamic([{row, current_binding}], field(row, ^attribute.name) in ^values)
end
defp filter_value_to_dynamic_expr(attribute, %IsNil{nil?: true}, _type, current_binding) do
Ecto.Query.dynamic([{row, current_binding}], is_nil(field(row, ^attribute.name)))
end
defp filter_value_to_dynamic_expr(attribute, %IsNil{nil?: false}, _type, current_binding) do
Ecto.Query.dynamic([{row, current_binding}], not is_nil(field(row, ^attribute.name)))
end
defp filter_value_to_dynamic_expr(attribute, %Trigram{} = trigram, _type, current_binding) do
case trigram do
%{equals: nil, greater_than: greater_than, less_than: nil, text: text} ->
Ecto.Query.dynamic(
[{row, current_binding}],
fragment("similarity(?, ?) > ?", field(row, ^attribute.name), ^text, ^greater_than)
)
%{equals: nil, greater_than: nil, less_than: less_than, text: text} ->
Ecto.Query.dynamic(
[{row, current_binding}],
fragment("similarity(?, ?) < ?", field(row, ^attribute.name), ^text, ^less_than)
)
%{equals: nil, greater_than: greater_than, less_than: less_than, text: text} ->
Ecto.Query.dynamic(
[{row, current_binding}],
fragment(
"similarity(?, ?) BETWEEN ? AND ?",
field(row, ^attribute.name),
^text,
^less_than,
^greater_than
)
)
%{equals: equals, text: text} ->
Ecto.Query.dynamic(
[{row, current_binding}],
fragment("similarity(?, ?) = ?", field(row, ^attribute.name), ^text, ^equals)
)
end
end
# IMPORTANT: We need to rework this so we don't need this hacky logic.
# Specifically, we can't use dynamic expers in selects, so we need this for aggregates :(
defp filter_to_expr(%Filter{expression: expression}, bindings, params) do
filter_to_expr(expression, bindings, params)
end
@ -962,7 +887,7 @@ defmodule AshPostgres.DataLayer do
case attribute do
%Ash.Resource.Attribute{} ->
Enum.find_value(bindings, fn {binding, data} ->
data.path == relationship_path && data.type in [:left, :root] && binding
data.path == relationship_path && data.type in [:inner, :left, :root] && binding
end)
%Ash.Query.Aggregate{} = aggregate ->
@ -971,60 +896,111 @@ defmodule AshPostgres.DataLayer do
end)
end
type = Ash.Type.ecto_type(attribute.type)
filter_value_to_expr(attribute.name, predicate, type, current_binding, params)
filter_value_to_expr(
attribute.name,
predicate,
attribute.type,
current_binding,
params,
pred.embedded
)
end
defp filter_value_to_expr(attribute, %Eq{value: value}, type, current_binding, params) do
defp filter_value_to_expr(
attribute,
%Eq{value: value},
type,
current_binding,
params,
embedded?
) do
simple_operator_expr(
:==,
params,
value,
type,
current_binding,
attribute
attribute,
embedded?
)
end
defp filter_value_to_expr(attribute, %LessThan{value: value}, type, current_binding, params) do
defp filter_value_to_expr(
attribute,
%LessThan{value: value},
type,
current_binding,
params,
embedded?
) do
simple_operator_expr(
:<,
params,
value,
type,
current_binding,
attribute
attribute,
embedded?
)
end
defp filter_value_to_expr(attribute, %GreaterThan{value: value}, type, current_binding, params) do
defp filter_value_to_expr(
attribute,
%GreaterThan{value: value},
type,
current_binding,
params,
embedded?
) do
simple_operator_expr(
:>,
params,
value,
type,
current_binding,
attribute
attribute,
embedded?
)
end
defp filter_value_to_expr(attribute, %In{values: values}, type, current_binding, params) do
defp filter_value_to_expr(
attribute,
%In{values: values},
type,
current_binding,
params,
embedded?
) do
simple_operator_expr(
:in,
params,
values,
{:in, type},
current_binding,
attribute
attribute,
embedded?
)
end
defp filter_value_to_expr(attribute, %IsNil{nil?: true}, _type, current_binding, params) do
defp filter_value_to_expr(
attribute,
%IsNil{nil?: true},
_type,
current_binding,
params,
_embedded?
) do
{params, {:is_nil, [], [{{:., [], [{:&, [], [current_binding]}, attribute]}, [], []}]}}
end
defp filter_value_to_expr(attribute, %IsNil{nil?: false}, _type, current_binding, params) do
defp filter_value_to_expr(
attribute,
%IsNil{nil?: false},
_type,
current_binding,
params,
_embedded?
) do
{params,
{:not, [], [{:is_nil, [], [{{:., [], [{:&, [], [current_binding]}, attribute]}, [], []}]}]}}
end
@ -1034,13 +1010,14 @@ defmodule AshPostgres.DataLayer do
%Trigram{} = trigram,
_type,
current_binding,
params
params,
false
) do
param_count = Enum.count(params)
case trigram do
%{equals: equals, greater_than: nil, less_than: nil, text: text} ->
{params ++ [{text, {current_binding, attribute}}, {equals, :float}],
{params ++ [{text, :string}, {equals, :float}],
{:fragment, [],
[
raw: "similarity(",
@ -1053,7 +1030,7 @@ defmodule AshPostgres.DataLayer do
]}}
%{equals: nil, greater_than: greater_than, less_than: nil, text: text} ->
{params ++ [{text, {current_binding, attribute}}, {greater_than, :float}],
{params ++ [{text, :string}, {greater_than, :float}],
{:fragment, [],
[
raw: "similarity(",
@ -1066,7 +1043,7 @@ defmodule AshPostgres.DataLayer do
]}}
%{equals: nil, greater_than: nil, less_than: less_than, text: text} ->
{params ++ [{text, {current_binding, attribute}}, {less_than, :float}],
{params ++ [{text, :string}, {less_than, :float}],
{:fragment, [],
[
raw: "similarity(",
@ -1080,7 +1057,7 @@ defmodule AshPostgres.DataLayer do
%{equals: nil, greater_than: greater_than, less_than: less_than, text: text} ->
{params ++
[{text, {current_binding, attribute}}, {less_than, :float}, {greater_than, :float}],
[{text, :string}, {less_than, :float}, {greater_than, :float}],
{:fragment, [],
[
raw: "similarity(",
@ -1096,8 +1073,73 @@ defmodule AshPostgres.DataLayer do
end
end
defp simple_operator_expr(op, params, value, type, current_binding, attribute) do
{params ++ [{value, type}],
defp filter_value_to_expr(
attribute,
%Trigram{} = trigram,
_type,
current_binding,
params,
true
) do
case trigram do
%{equals: equals, greater_than: nil, less_than: nil, text: text} ->
{params,
{:fragment, [],
[
raw: "similarity(",
expr: {{:., [], [{:&, [], [current_binding]}, attribute]}, [], []},
raw: ", ",
expr: tagged(text, :string),
raw: ") = ",
expr: tagged(equals, :float),
raw: ""
]}}
%{equals: nil, greater_than: greater_than, less_than: nil, text: text} ->
{params,
{:fragment, [],
[
raw: "similarity(",
expr: {{:., [], [{:&, [], [current_binding]}, attribute]}, [], []},
raw: ", ",
expr: tagged(text, :string),
raw: ") > ",
expr: tagged(greater_than, :float),
raw: ""
]}}
%{equals: nil, greater_than: nil, less_than: less_than, text: text} ->
{params,
{:fragment, [],
[
raw: "similarity(",
expr: {{:., [], [{:&, [], [current_binding]}, attribute]}, [], []},
raw: ", ",
expr: tagged(text, :string),
raw: ") < ",
expr: tagged(less_than, :float),
raw: ""
]}}
%{equals: nil, greater_than: greater_than, less_than: less_than, text: text} ->
{params,
{:fragment, [],
[
raw: "similarity(",
expr: {{:., [], [{:&, [], [current_binding]}, attribute]}, [], []},
raw: ", ",
expr: tagged(text, :string),
raw: ") BETWEEN ",
expr: tagged(less_than, :float),
raw: " AND ",
expr: tagged(greater_than, :float),
raw: ""
]}}
end
end
defp simple_operator_expr(op, params, value, type, current_binding, attribute, false) do
{params ++ [{value, Ash.Type.ecto_type(type)}],
{op, [],
[
{{:., [], [{:&, [], [current_binding]}, attribute]}, [], []},
@ -1105,6 +1147,31 @@ defmodule AshPostgres.DataLayer do
]}}
end
defp simple_operator_expr(op, params, value, type, current_binding, attribute, true) do
{params,
{op, [],
[
{{:., [], [{:&, [], [current_binding]}, attribute]}, [], []},
tagged(value, type)
]}}
end
defp tagged(value, type) do
%Ecto.Query.Tagged{value: value, type: get_type(type)}
end
defp get_type({:array, type}) do
{:array, get_type(type)}
end
defp get_type(type) do
if Ash.Type.ash_type?(type) do
Ash.Type.storage_type(type)
else
type
end
end
defp add_binding(query, data) do
current = query.__ash_bindings__.current
bindings = query.__ash_bindings__.bindings
@ -1129,6 +1196,9 @@ defmodule AshPostgres.DataLayer do
end
defp maybe_get_resource_query(resource) do
{table(resource), resource}
case Ash.Query.data_layer_query(Ash.Query.new(resource), only_validate_filter?: false) do
{:ok, query} -> query
{:error, error} -> {:error, error}
end
end
end

View file

@ -10,7 +10,11 @@ defmodule AshPostgres.MigrationGenerator do
alias AshPostgres.MigrationGenerator.{Operation, Phase}
defstruct snapshot_path: @default_snapshot_path, migration_path: nil, quiet: false, format: true
defstruct snapshot_path: @default_snapshot_path,
migration_path: nil,
quiet: false,
format: true,
dry_run: false
def generate(apis, opts \\ []) do
apis = List.wrap(apis)
@ -78,8 +82,19 @@ defmodule AshPostgres.MigrationGenerator do
new_snapshot.identities
|> Kernel.++(identities)
|> Enum.sort_by(& &1.name)
# We sort the identities by there being an identity with a matching name in the existing snapshot
# so that we prefer identities that currently exist over new ones
|> Enum.sort_by(fn identity ->
existing_snapshot
|> Kernel.||(%{})
|> Map.get(:identities, [])
|> Enum.any?(fn existing_identity ->
existing_identity.name == identity.name
end)
|> Kernel.!()
end)
|> Enum.uniq_by(fn identity ->
Enum.sort(identity.keys)
{Enum.sort(identity.keys), identity.base_filter}
end)
new_snapshot = %{new_snapshot | identities: all_identities}
@ -253,17 +268,19 @@ defmodule AshPostgres.MigrationGenerator do
defp write_migration({up, down}, snapshots, repo, opts) do
repo_name = repo |> Module.split() |> List.last() |> Macro.underscore()
Enum.each(snapshots, fn snapshot ->
snapshot_binary = snapshot_to_binary(snapshot)
unless opts.dry_run do
Enum.each(snapshots, fn snapshot ->
snapshot_binary = snapshot_to_binary(snapshot)
snapshot_file =
opts.snapshot_path
|> Path.join(repo_name)
|> Path.join(snapshot.table <> ".json")
snapshot_file =
opts.snapshot_path
|> Path.join(repo_name)
|> Path.join(snapshot.table <> ".json")
File.mkdir_p(Path.dirname(snapshot_file))
File.write!(snapshot_file, snapshot_binary, [])
end)
File.mkdir_p(Path.dirname(snapshot_file))
File.write!(snapshot_file, snapshot_binary, [])
end)
end
migration_path =
if opts.migration_path do
@ -309,7 +326,11 @@ defmodule AshPostgres.MigrationGenerator do
end
"""
create_file(migration_file, format(contents, opts))
if opts.dry_run do
Mix.shell().info(format(contents, opts))
else
create_file(migration_file, format(contents, opts))
end
end
defp build_up_and_down(phases) do
@ -419,6 +440,10 @@ defmodule AshPostgres.MigrationGenerator do
group_into_phases(rest, %{phase | operations: [op | phase.operations]}, acc)
end
defp group_into_phases([%{no_phase: true} = op | rest], nil, acc) do
group_into_phases(rest, nil, [op | acc])
end
defp group_into_phases([operation | rest], nil, acc) do
group_into_phases(rest, nil, [
%Phase.Alter{operations: [operation], table: operation.table} | acc
@ -594,7 +619,8 @@ defmodule AshPostgres.MigrationGenerator do
old_snapshot.identities
|> Enum.reject(fn old_identity ->
Enum.find(snapshot.identities, fn identity ->
Enum.sort(old_identity.keys) == Enum.sort(identity.keys)
Enum.sort(old_identity.keys) == Enum.sort(identity.keys) &&
old_identity.base_filter == identity.base_filter
end)
end)
|> Enum.map(fn identity ->
@ -605,11 +631,15 @@ defmodule AshPostgres.MigrationGenerator do
snapshot.identities
|> Enum.reject(fn identity ->
Enum.find(old_snapshot.identities, fn old_identity ->
Enum.sort(old_identity.keys) == Enum.sort(identity.keys)
Enum.sort(old_identity.keys) == Enum.sort(identity.keys) &&
old_identity.base_filter == identity.base_filter
end)
end)
|> Enum.map(fn identity ->
%Operation.AddUniqueIndex{identity: identity, table: snapshot.table}
%Operation.AddUniqueIndex{
identity: identity,
table: snapshot.table
}
end)
attribute_operations ++ unique_indexes_to_add ++ unique_indexes_to_remove ++ acc
@ -772,7 +802,8 @@ defmodule AshPostgres.MigrationGenerator do
attributes: attributes(resource),
identities: identities(resource),
table: AshPostgres.table(resource),
repo: AshPostgres.repo(resource)
repo: AshPostgres.repo(resource),
base_filter: AshPostgres.base_filter_sql(resource)
}
hash =
@ -834,13 +865,35 @@ defmodule AshPostgres.MigrationGenerator do
defp identities(resource) do
resource
|> Ash.Resource.identities()
|> case do
[] ->
[]
identities ->
base_filter = Ash.Resource.base_filter(resource)
if base_filter && !AshPostgres.base_filter_sql(resource) do
raise """
Currently, ash_postgres cannot translate your base_filter #{inspect(base_filter)} into sql. You must provide the `base_filter_sql` option, or skip unique indexes with `skip_unique_indexes`"
"""
end
identities
end
|> Enum.reject(fn identity ->
identity.name in AshPostgres.skip_unique_indexes?(resource)
end)
|> Enum.filter(fn identity ->
Enum.all?(identity.keys, fn key ->
Ash.Resource.attribute(resource, key)
end)
end)
|> Enum.map(fn identity ->
%{identity | keys: Enum.sort(identity.keys)}
end)
|> Enum.sort_by(& &1.name)
|> Enum.map(&Map.take(&1, [:name, :keys]))
|> Enum.map(&Map.put(&1, :base_filter, AshPostgres.base_filter_sql(resource)))
end
if :erlang.function_exported(Ash, :uuid, 0) do
@ -904,7 +957,10 @@ defmodule AshPostgres.MigrationGenerator do
identity
|> Map.update!(:name, &String.to_atom/1)
|> Map.update!(:keys, fn keys ->
Enum.map(keys, &String.to_atom/1)
keys
|> Enum.map(&String.to_atom/1)
|> Enum.sort()
end)
|> Map.put_new(:base_filter, nil)
end
end

View file

@ -111,30 +111,35 @@ defmodule AshPostgres.MigrationGenerator.Operation do
defmodule AddUniqueIndex do
@moduledoc false
defstruct [:identity, :table]
defstruct [:identity, :table, no_phase: true]
def up(%{identity: %{name: name, keys: keys}, table: table}) do
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{table}_#{
name
}_unique_index\")"
def up(%{identity: %{name: name, keys: keys, base_filter: base_filter}, table: table}) do
if base_filter do
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{
table
}_#{name}_unique_index\", where: \"#{base_filter}\")"
else
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{
table
}_#{name}_unique_index\")"
end
end
def down(%{identity: %{name: name, keys: keys}, table: table}) do
# {
"drop unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{table}_#{
name
}_unique_index\")"
"drop_if_exists unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{
table
}_#{name}_unique_index\")"
end
end
defmodule RemoveUniqueIndex do
@moduledoc false
defstruct [:identity, :table]
defstruct [:identity, :table, no_phase: true]
def up(%{identity: %{name: name, keys: keys}, table: table}) do
"drop unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{table}_#{
name
}_unique_index\")"
"drop_if_exists unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{
table
}_#{name}_unique_index\")"
end
def down(%{identity: %{name: name, keys: keys}, table: table}) do

View file

@ -12,8 +12,8 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
Flags:
* `quiet` - messages for file creations will not be printed
* `format` - files that are created will be formatted with the code formatter, defaults to true
* `no_format` - files that are created will not be formatted with the code formatter
* `dry_run` - no files are created, instead the new migration is printed
#### Conflicts/Multiple Resources
@ -52,7 +52,8 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
snapshot_path: :string,
migration_path: :string,
quiet: :boolean,
format: :boolean
no_format: :boolean,
dry_run: :boolean
]
)
@ -83,6 +84,11 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
raise "must supply the --apis argument, or set `config :my_app, apis: [...]` in config"
end
opts =
opts
|> Keyword.put(:format, !opts[:no_format])
|> Keyword.delete(:no_format)
AshPostgres.MigrationGenerator.generate(apis, opts)
end

View file

@ -66,7 +66,7 @@ defmodule AshPostgres.MixProject do
[
{:ecto_sql, "~> 3.4"},
{:postgrex, ">= 0.0.0"},
{:ash, ash_version("~> 1.13.2")},
{:ash, ash_version("~> 1.13.3")},
{:git_ops, "~> 2.0.1", only: :dev},
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
{:ex_check, "~> 0.11.0", only: :dev},

View file

@ -1,12 +1,12 @@
%{
"ash": {:hex, :ash, "1.13.2", "e3f0f2d831e69f956f78e69501cff39e3701566e6f00bf21c01ad8c172eeac0c", [:mix], [{:ecto, "~> 3.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.1.4", [hex: :picosat_elixir, repo: "hexpm", optional: false]}], "hexpm", "e3d44a9f123d126ced1614ba69a62af092210de78ddb64be6dc8849a850be158"},
"ash": {:hex, :ash, "1.13.3", "b44000ff10d057179c92b171ba89f950cc1a743b1e6f10d8a3029ba026bd6770", [:mix], [{:ecto, "~> 3.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.1.4", [hex: :picosat_elixir, repo: "hexpm", optional: false]}], "hexpm", "bdc2a4df6bd2adf8dc586d5cead740cfa5cce732b3a98f4a0ca8b1614b947462"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"},
"dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"},
"earmark": {:hex, :earmark, "1.4.5", "62ffd3bd7722fb7a7b1ecd2419ea0b458c356e7168c1f5d65caf09b4fbdd13c8", [:mix], [], "hexpm", "b7d0e6263d83dc27141a523467799a685965bf8b13b6743413f19a7079843f4f"},
"ecto": {:hex, :ecto, "3.4.6", "08f7afad3257d6eb8613309af31037e16c36808dfda5a3cd0cb4e9738db030e4", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6f13a9e2a62e75c2dcfc7207bfc65645ab387af8360db4c89fee8b5a4bf3f70b"},
@ -21,7 +21,7 @@
"git_ops": {:hex, :git_ops, "2.0.1", "9d3df6c710a80a8779dbb144c79fb24c777660ae862cc454ab3193afd0c02a37", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cd499a72523ba338c20973eadb707d25a42e4a77c46d2ff5c45e61e7adae6190"},
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
"machinery": {:hex, :machinery, "1.0.0", "df6968d84c651b9971a33871c78c10157b6e13e4f3390b0bee5b0e8bdea8c781", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "4f6eb4185a48e7245360bedf653af4acc6fa6ae8ff4690619395543fa1a8395f"},
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},