chore: perf fixes, build, and test

This commit is contained in:
Zach Daniel 2024-09-05 00:31:38 -04:00
parent f80b157d06
commit 9191bd3269
7 changed files with 68 additions and 89 deletions

View file

@ -25,8 +25,8 @@ defmodule AshPostgres.Igniter do
{igniter, {:ok, value}} when is_binary(value) or is_nil(value) ->
{:ok, igniter, value}
_ ->
:error
{igniter, _} ->
{:error, igniter}
end
end
@ -37,8 +37,8 @@ defmodule AshPostgres.Igniter do
{igniter, {:ok, value}} when is_atom(value) ->
{:ok, igniter, value}
_ ->
:error
{igniter, _} ->
{:error, igniter}
end
end

View file

@ -5,7 +5,7 @@ defmodule Mix.Tasks.AshPostgres.Gen.Resources do
@shortdoc "Generates or updates resources based on a database schema"
@doc """
@moduledoc """
#{@shortdoc}
## Example

View file

@ -1,4 +1,5 @@
defmodule AshPostgres.ResourceGenerator do
@moduledoc false
alias AshPostgres.ResourceGenerator.Spec
require Logger
@ -6,26 +7,9 @@ defmodule AshPostgres.ResourceGenerator do
def generate(igniter, repos, domain, opts \\ []) do
{igniter, resources} = Ash.Resource.Igniter.list_resources(igniter)
resources =
Task.async_stream(resources, fn resource ->
{resource, AshPostgres.Igniter.repo(igniter, resource),
AshPostgres.Igniter.table(igniter, resource)}
end)
|> Enum.map(fn {:ok, {resource, repo, table}} ->
repo =
case repo do
{:ok, _igniter, repo} -> repo
_ -> nil
end
table =
case table do
{:ok, _igniter, table} -> table
_ -> nil
end
{resource, repo, table}
end)
# This is a hack. We should be looking at compiled resources
# unlikely to ever matter given how this task will be used though.
resources = Enum.filter(resources, &Code.ensure_loaded?/1)
igniter = Igniter.include_all_elixir_files(igniter)
@ -140,8 +124,6 @@ defmodule AshPostgres.ResourceGenerator do
end
defp check_constraints(%{check_constraints: check_constraints}, _) do
IO.inspect(check_constraints)
check_constraints =
Enum.map_join(check_constraints, "\n", fn check_constraint ->
"""
@ -158,9 +140,8 @@ defmodule AshPostgres.ResourceGenerator do
defp skip_unique_indexes(%{indexes: indexes}) do
indexes
|> Enum.filter(& &1.unique?)
|> Enum.filter(fn %{columns: columns} ->
Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
|> Enum.filter(fn %{unique?: unique?, columns: columns} ->
unique? && Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
end)
|> Enum.reject(&index_as_identity?/1)
|> case do
@ -176,9 +157,8 @@ defmodule AshPostgres.ResourceGenerator do
defp identity_index_names(%{indexes: indexes}) do
indexes
|> Enum.filter(& &1.unique?)
|> Enum.filter(fn %{columns: columns} ->
Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
|> Enum.filter(fn %{unique?: unique?, columns: columns} ->
unique? && Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
end)
|> case do
[] ->
@ -195,9 +175,8 @@ defmodule AshPostgres.ResourceGenerator do
defp add_identities(str, %{indexes: indexes}) do
indexes
|> Enum.filter(& &1.unique?)
|> Enum.filter(fn %{columns: columns} ->
Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
|> Enum.filter(fn %{unique?: unique?, columns: columns} ->
unique? && Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
end)
|> Enum.map(fn index ->
name = index.name
@ -431,10 +410,8 @@ defmodule AshPostgres.ResourceGenerator do
defp custom_indexes(table_spec, true) do
table_spec.indexes
|> Enum.reject(fn index ->
!index.unique? || (&index_as_identity?/1)
end)
|> Enum.reject(fn index ->
Enum.any?(index.columns, &String.contains?(&1, "("))
!index.unique? || (&index_as_identity?/1) ||
Enum.any?(index.columns, &String.contains?(&1, "("))
end)
|> case do
[] ->

View file

@ -1,4 +1,5 @@
defmodule AshPostgres.ResourceGenerator.SensitiveData do
@moduledoc false
# I got this from ChatGPT, but this is a best effort transformation
# anyway.
@sensitive_patterns [
@ -65,7 +66,7 @@ defmodule AshPostgres.ResourceGenerator.SensitiveData do
~r/.*_token/i
]
def is_sensitive?(column_name) do
def sensitive?(column_name) do
Enum.any?(@sensitive_patterns, fn pattern ->
Regex.match?(pattern, column_name)
end)

View file

@ -1,4 +1,5 @@
defmodule AshPostgres.ResourceGenerator.Spec do
@moduledoc false
require Logger
defstruct [
@ -15,6 +16,7 @@ defmodule AshPostgres.ResourceGenerator.Spec do
]
defmodule Attribute do
@moduledoc false
defstruct [
:name,
:type,
@ -31,6 +33,7 @@ defmodule AshPostgres.ResourceGenerator.Spec do
end
defmodule ForeignKey do
@moduledoc false
defstruct [
:constraint_name,
:match_type,
@ -44,14 +47,17 @@ defmodule AshPostgres.ResourceGenerator.Spec do
end
defmodule Index do
@moduledoc false
defstruct [:name, :columns, :unique?, :nulls_distinct, :where_clause, :using, :include]
end
defmodule CheckConstraint do
@moduledoc false
defstruct [:name, :column, :expression]
end
defmodule Relationship do
@moduledoc false
defstruct [
:name,
:type,
@ -71,7 +77,6 @@ defmodule AshPostgres.ResourceGenerator.Spec do
Ecto.Migrator.with_repo(repo, fn repo ->
repo
|> table_specs(opts)
|> verify_found_tables(opts)
|> Enum.group_by(&Enum.take(&1, 2), fn [_, _, field, type, default, size, allow_nil?] ->
name = Macro.underscore(field)
@ -421,39 +426,6 @@ defmodule AshPostgres.ResourceGenerator.Spec do
raise "Unexpected character: #{inspect(other)} at #{inspect(stack)} with #{inspect(field)} - #{inspect(acc)}"
end
defp verify_found_tables(specs, opts) do
if opts[:tables] do
not_found =
Enum.reject(opts[:tables], fn table ->
if String.ends_with?(table, ".") do
true
else
case String.split(table, ".") do
[schema, table] ->
Enum.any?(specs, fn spec ->
Enum.at(spec, 0) == table and Enum.at(spec, 1) == schema
end)
[table] ->
Enum.any?(specs, fn spec ->
Enum.at(spec, 0) == table
end)
end
end
end)
case not_found do
[] ->
specs
tables ->
raise "The following tables did not exist: #{inspect(tables)}"
end
else
specs
end
end
defp build_attributes(attributes, table_name, schema, repo, opts) do
attributes
|> set_primary_key(table_name, schema, repo)
@ -546,9 +518,9 @@ defmodule AshPostgres.ResourceGenerator.Spec do
|> Enum.flat_map(fn {repo, specs} ->
do_add_relationships(
specs,
Enum.flat_map(resources, fn {resource, resource_repo, table} ->
if resource_repo == repo do
[{resource, table}]
Enum.flat_map(resources, fn resource ->
if AshPostgres.DataLayer.Info.repo(resource) == repo do
[{resource, AshPostgres.DataLayer.Info.table(resource)}]
else
[]
end
@ -918,7 +890,7 @@ defmodule AshPostgres.ResourceGenerator.Spec do
Enum.map(attributes, fn attribute ->
%{
attribute
| sensitive?: AshPostgres.ResourceGenerator.SensitiveData.is_sensitive?(attribute.name)
| sensitive?: AshPostgres.ResourceGenerator.SensitiveData.sensitive?(attribute.name)
}
end)
end

View file

@ -3,6 +3,7 @@ defmodule AshPostgres.RelWithParentFilterTest do
setup do
AshPostgres.TestRepo.query!("DROP TABLE IF EXISTS example_table")
AshPostgres.TestRepo.query!("CREATE TABLE example_table (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(255),
@ -10,18 +11,45 @@ defmodule AshPostgres.RelWithParentFilterTest do
email VARCHAR(255)
)")
on_exit(fn ->
AshPostgres.TestRepo.query!("DROP TABLE IF EXISTS example_table")
end)
:ok
end
test "a resource is generated from a table" do
Igniter.new()
|> Igniter.compose_task("ash_postgres.gen.resources", [
"MyApp.Accounts",
"--tables",
"example_table",
"--yes"
])
resource =
Igniter.new()
|> Igniter.compose_task("ash_postgres.gen.resources", [
"MyApp.Accounts",
"--tables",
"example_table",
"--yes"
])
|> Igniter.prepare_for_write()
|> Map.get(:rewrite)
|> Rewrite.source!("lib/my_app/accounts/example_table.ex")
|> Rewrite.Source.get(:content)
assert String.trim(resource) ==
String.trim("""
defmodule MyApp.Accounts.ExampleTable do
use Ash.Resource,
domain: MyApp.Accounts,
data_layer: AshPostgres.DataLayer
postgres do
table "example_table"
repo AshPostgres.TestRepo
end
attributes do
uuid_primary_key(:id)
attribute(:name, :string)
attribute(:age, :integer)
attribute :email, :string do
sensitive?(true)
end
end
end
""")
end
end

View file

@ -50,6 +50,7 @@ defmodule HasNoComments do
end
defmodule CiCategory do
@moduledoc false
use Ash.Type.NewType,
subtype_of: :ci_string
end