improvement: custom-extension implementation (#162)

* improvement: custom-extension implementation

* improvement: allow adding custom-extension by module's reference and fixes formatting

* ci: fixes formatter
This commit is contained in:
Alessio Montagnani 2023-08-08 19:20:26 +02:00 committed by GitHub
parent 7ce33cac2c
commit 4a02d8c6ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 159 additions and 22 deletions

20
lib/custom_extension.ex Normal file
View file

@ -0,0 +1,20 @@
defmodule AshPostgres.CustomExtension do
@moduledoc """
A custom extension implementation.
"""
@callback install(version :: integer) :: String.t()
@callback uninstall(version :: integer) :: String.t()
defmacro __using__(name: name, latest_version: latest_version) do
quote do
@behaviour AshPostgres.CustomExtension
@extension_name unquote(name)
@extension_latest_version unquote(latest_version)
def extension, do: {@extension_name, @extension_latest_version, &install/1, &uninstall/1}
end
end
end

View file

@ -1106,8 +1106,7 @@ defmodule AshPostgres.Expr do
Map.get(first_relationship, :manual) ->
{module, opts} = first_relationship.manual
[pkey_attr | _] =
Ash.Resource.Info.primary_key(first_relationship.destination)
[pkey_attr | _] = Ash.Resource.Info.primary_key(first_relationship.destination)
pkey_attr = Ash.Resource.Info.attribute(first_relationship.destination, pkey_attr)

View file

@ -264,16 +264,15 @@ defmodule AshPostgres.Join do
)
end
parent_bindings =
%{
base_bindings
| resource: relationship.source,
calculations: %{},
parent_resources: [],
aggregate_defs: %{},
context: relationship.context,
current: parent_binding + 1
}
parent_bindings = %{
base_bindings
| resource: relationship.source,
calculations: %{},
parent_resources: [],
aggregate_defs: %{},
context: relationship.context,
current: parent_binding + 1
}
parent_bindings =
if bindings do

View file

@ -180,10 +180,25 @@ defmodule AshPostgres.MigrationGenerator do
{other, other.installed}
end
to_install = List.wrap(repo.installed_extensions()) -- List.wrap(installed_extensions)
requesteds =
repo.installed_extensions()
|> Enum.map(fn
extension_module when is_atom(extension_module) ->
{ext_name, version, _up_fn, _down_fn} = extension = extension_module.extension()
{"#{ext_name}_v#{version}", extension}
extension_name ->
{extension_name, extension_name}
end)
to_install =
if "ash-functions" in repo.installed_extensions() &&
requesteds
|> Enum.filter(fn {name, _extension} -> !Enum.member?(installed_extensions, name) end)
|> Enum.map(fn {_name, extension} -> extension end)
to_install =
if "ash-functions" in requesteds &&
extensions_snapshot[:ash_functions_version] !=
@latest_ash_functions_version do
Enum.uniq(["ash-functions" | to_install])
@ -197,6 +212,10 @@ defmodule AshPostgres.MigrationGenerator do
else
{module, migration_name} =
case to_install do
[{ext_name, version, _up_fn, _down_fn}] ->
{"install_#{ext_name}_v#{version}",
"#{timestamp(true)}_install_#{ext_name}_v#{version}_extension"}
[single] ->
{"install_#{single}", "#{timestamp(true)}_install_#{single}_extension"}
@ -222,6 +241,9 @@ defmodule AshPostgres.MigrationGenerator do
"ash-functions" ->
install_ash_functions(extensions_snapshot[:ash_functions_version])
{_ext_name, version, up_fn, _down_fn} when is_function(up_fn, 1) ->
up_fn.(version)
extension ->
"execute(\"CREATE EXTENSION IF NOT EXISTS \\\"#{extension}\\\"\")"
end)
@ -231,6 +253,9 @@ defmodule AshPostgres.MigrationGenerator do
"ash-functions" ->
"execute(\"DROP FUNCTION IF EXISTS ash_elixir_and(BOOLEAN, ANYCOMPATIBLE), ash_elixir_and(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(BOOLEAN, ANYCOMPATIBLE)\")"
{_ext_name, version, _up_fn, down_fn} when is_function(down_fn, 1) ->
down_fn.(version)
extension ->
"# execute(\"DROP EXTENSION IF EXISTS \\\"#{extension}\\\"\")"
end)
@ -257,12 +282,14 @@ defmodule AshPostgres.MigrationGenerator do
end
"""
installed = Enum.map(requesteds, fn {name, _extension} -> name end)
snapshot_contents =
Jason.encode!(
%{
installed: repo.installed_extensions()
installed: installed
}
|> set_ash_functions(repo.installed_extensions()),
|> set_ash_functions(installed),
pretty: true
)

View file

@ -4,6 +4,7 @@
"ash-functions",
"uuid-ossp",
"pg_trgm",
"citext"
"citext",
"demo-functions_v1"
]
}

View file

@ -0,0 +1,26 @@
defmodule AshPostgres.TestRepo.Migrations.InstallDemoFunctionsV0 do
@moduledoc """
Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
execute("""
CREATE OR REPLACE FUNCTION ash_demo_functions()
RETURNS boolean AS $$ SELECT TRUE $$
LANGUAGE SQL
IMMUTABLE;
""")
end
def down do
# Uncomment this if you actually want to uninstall the extensions
# when this migration is rolled back:
execute("""
DROP FUNCTION IF EXISTS ash_demo_functions()
""")
end
end

View file

@ -0,0 +1,26 @@
defmodule AshPostgres.TestRepo.Migrations.InstallDemoFunctionsV1 do
@moduledoc """
Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
execute("""
CREATE OR REPLACE FUNCTION ash_demo_functions()
RETURNS boolean AS $$ SELECT FALSE $$
LANGUAGE SQL
IMMUTABLE;
""")
end
def down do
# Uncomment this if you actually want to uninstall the extensions
# when this migration is rolled back:
execute("""
DROP FUNCTION IF EXISTS ash_demo_functions()
""")
end
end

View file

@ -0,0 +1,38 @@
defmodule AshPostgres.TestCustomExtension do
@moduledoc false
use AshPostgres.CustomExtension, name: "demo-functions", latest_version: 1
@impl true
def install(0) do
"""
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_demo_functions()
RETURNS boolean AS $$ SELECT TRUE $$
LANGUAGE SQL
IMMUTABLE;
\"\"\")
"""
end
@impl true
def install(1) do
"""
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_demo_functions()
RETURNS boolean AS $$ SELECT FALSE $$
LANGUAGE SQL
IMMUTABLE;
\"\"\")
"""
end
@impl true
def uninstall(_version) do
"""
execute(\"\"\"
DROP FUNCTION IF EXISTS ash_demo_functions()
\"\"\")
"""
end
end

View file

@ -8,7 +8,7 @@ defmodule AshPostgres.TestNoSandboxRepo do
end
def installed_extensions do
["ash-functions", "uuid-ossp", "pg_trgm", "citext"] --
["ash-functions", "uuid-ossp", "pg_trgm", "citext", AshPostgres.TestCustomExtension] --
Application.get_env(:ash_postgres, :no_extensions, [])
end

View file

@ -8,7 +8,7 @@ defmodule AshPostgres.TestRepo do
end
def installed_extensions do
["ash-functions", "uuid-ossp", "pg_trgm", "citext"] --
["ash-functions", "uuid-ossp", "pg_trgm", "citext", AshPostgres.TestCustomExtension] --
Application.get_env(:ash_postgres, :no_extensions, [])
end

View file

@ -1,9 +1,10 @@
{
"ash_functions_version": 1,
"installed": [
"ash-functions",
"uuid-ossp",
"pg_trgm",
"citext"
],
"ash_functions_version": 1
"citext",
"demo-functions_v1"
]
}