improvement: check_migrations, rename to --check

fix: don't use `table` where we should use `schema` in migration generator
This commit is contained in:
Zach Daniel 2022-05-18 13:21:58 -04:00
parent 610e38812d
commit 2a5c7ac4a2
16 changed files with 752 additions and 10 deletions

View file

@ -11,7 +11,8 @@
## ...or adjusted (e.g. use one-line formatter for more compact credo output)
# {:credo, "mix credo --format oneline"},
{:check_formatter, command: "mix ash.formatter --check"}
{:check_formatter, command: "mix ash.formatter --check"},
{:check_migrations, command: "mix test.check_migrations"}
## custom new tools may be added (mix tasks or arbitrary commands)
# {:my_mix_task, command: "mix release", env: %{"MIX_ENV" => "prod"}},
# {:my_arbitrary_tool, command: "npm test", cd: "assets"},

View file

@ -19,7 +19,7 @@ jobs:
otp: ["23"]
elixir: ["1.13.2"]
ash: ["main", "1.52.0-rc.1"]
pg_version: ["9.6", "11"]
pg_version: ["9.6", "11", "12", "13"]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ASH_VERSION: ${{matrix.ash}}

View file

@ -22,7 +22,7 @@ defmodule AshPostgres.MigrationGenerator do
no_shell?: false,
format: true,
dry_run: false,
check_generated: false,
check: false,
drop_columns: false
def generate(apis, opts \\ []) do
@ -97,7 +97,7 @@ defmodule AshPostgres.MigrationGenerator do
defp opts(opts) do
case struct(__MODULE__, opts) do
%{check_generated: true} = opts ->
%{check: true} = opts ->
%{opts | dry_run: true}
opts ->
@ -214,7 +214,16 @@ defmodule AshPostgres.MigrationGenerator do
:ok
operations ->
if opts.check_generated, do: exit({:shutdown, 1})
if opts.check do
IO.puts("""
Migrations would have been generated, but the --check flag was provided.
To see what migration would have been generated, run with the `--dry-run`
option instead. To generate those migrations, run without either flag.
""")
exit({:shutdown, 1})
end
operations
|> organize_operations
@ -1695,7 +1704,7 @@ defmodule AshPostgres.MigrationGenerator do
destination_field_generated: source_attribute.generated?,
multitenancy: multitenancy(relationship.source),
table: AshPostgres.table(relationship.source),
schema: AshPostgres.table(relationship.source),
schema: AshPostgres.schema(relationship.source),
on_delete: AshPostgres.polymorphic_on_delete(relationship.source),
on_update: AshPostgres.polymorphic_on_update(relationship.source),
name:

View file

@ -20,7 +20,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
* `quiet` - messages for file creations will not be printed
* `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
* `check-migrated` - no files are created, returns an exit(1) code if the current snapshots and resources don't fit
* `check` - no files are created, returns an exit(1) code if the current snapshots and resources don't fit
#### Snapshots
@ -83,7 +83,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
name: :string,
no_format: :boolean,
dry_run: :boolean,
check_migrated: :boolean,
check: :boolean,
drop_columns: :boolean
]
)

View file

@ -24,6 +24,7 @@ defmodule AshPostgres.MixProject do
"test.create": :test,
"test.migrate": :test,
"test.migrate_tenants": :test,
"test.check_migrations": :test,
"test.drop": :test,
"test.generate_migrations": :test,
"test.reset": :test
@ -124,6 +125,7 @@ defmodule AshPostgres.MixProject do
credo: "credo --strict",
"ash.formatter": "ash.formatter --extensions AshPostgres.DataLayer",
"test.generate_migrations": "ash_postgres.generate_migrations",
"test.check_migrations": "ash_postgres.generate_migrations --check",
"test.migrate_tenants": "ash_postgres.migrate --tenants",
"test.migrate": "ash_postgres.migrate",
"test.create": "ash_postgres.create",

View file

@ -0,0 +1,116 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "title",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "likes",
"type": "bigint"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "arbitrary_timestamp",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "created_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "special_name_fkey",
"on_delete": "delete",
"on_update": "update",
"schema": "public",
"table": "posts"
},
"size": null,
"source": "post_id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "comments_author_id_fkey",
"on_delete": null,
"on_update": null,
"schema": "public",
"table": "authors"
},
"size": null,
"source": "author_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "6EBA91B3BBCDE1477F386C62CEABB28AB9FE9B8E688CB849088145374DC1EB85",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "comments"
}

View file

@ -0,0 +1,66 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": true,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "post_links_source_post_id_fkey",
"on_delete": null,
"on_update": null,
"schema": "public",
"table": "posts"
},
"size": null,
"source": "source_post_id",
"type": "uuid"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": true,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "post_links_destination_post_id_fkey",
"on_delete": null,
"on_update": null,
"schema": "public",
"table": "posts"
},
"size": null,
"source": "destination_post_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "ABE3281B7C35040EA3BE51CA5475CA5AFD2840307E3541BE9B37D316A1F7898D",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "post_links"
}

View file

@ -0,0 +1,150 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "title",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "score",
"type": "bigint"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "public",
"type": "boolean"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "category",
"type": "citext"
},
{
"allow_nil?": true,
"default": "\"sponsored\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "type",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "price",
"type": "bigint"
},
{
"allow_nil?": true,
"default": "\"0\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "decimal",
"type": "decimal"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "status",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "status_enum",
"type": "status"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "point",
"type": [
"array",
"float"
]
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "created_at",
"type": "utc_datetime_usec"
}
],
"base_filter": "type = 'sponsored'",
"check_constraints": [
{
"attribute": [
"price"
],
"base_filter": "type = 'sponsored'",
"check": "price > 0",
"name": "price_must_be_positive"
}
],
"custom_indexes": [],
"has_create_action": true,
"hash": "9D6F943BF87E7AA79511EA5102F07D10972DF830089F0C10785C7518BA11943A",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "posts"
}

View file

@ -0,0 +1,86 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": "id",
"global": true,
"strategy": "attribute"
},
"name": "multitenant_posts_org_id_fkey",
"on_delete": null,
"on_update": null,
"schema": "public",
"table": "multitenant_orgs"
},
"size": null,
"source": "org_id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": "org_id",
"global": true,
"strategy": "attribute"
},
"name": "multitenant_posts_user_id_fkey",
"on_delete": null,
"on_update": null,
"schema": "public",
"table": "users"
},
"size": null,
"source": "user_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "B2BB130AF001CA540847811EDBDCE8E5A5C2B050EB1B951556338882E7B0619A",
"identities": [],
"multitenancy": {
"attribute": null,
"global": false,
"strategy": "context"
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "multitenant_posts"
}

View file

@ -0,0 +1,62 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": "id",
"global": true,
"strategy": "attribute"
},
"name": "users_org_id_fkey",
"on_delete": null,
"on_update": null,
"schema": "public",
"table": "multitenant_orgs"
},
"size": null,
"source": "org_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "B8098B00555AB33A5833083A543A54D937B3C4C76AFE35507729D823BB18A5E2",
"identities": [],
"multitenancy": {
"attribute": "org_id",
"global": true,
"strategy": "attribute"
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "users"
}

View file

@ -0,0 +1,130 @@
defmodule AshPostgres.TestRepo.Migrations.MigrateResources4 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
drop constraint(:users, "users_org_id_fkey")
alter table(:users) do
modify :org_id,
references(:multitenant_orgs,
column: :id,
name: "users_org_id_fkey",
type: :uuid,
prefix: "public"
)
end
alter table(:posts) do
modify :decimal, :decimal, default: "0"
add :point, {:array, :float}
end
drop constraint(:post_links, "post_links_destination_post_id_fkey")
drop constraint(:post_links, "post_links_source_post_id_fkey")
alter table(:post_links) do
modify :source_post_id,
references(:posts,
column: :id,
prefix: "public",
name: "post_links_source_post_id_fkey",
type: :uuid
)
end
alter table(:post_links) do
modify :destination_post_id,
references(:posts,
column: :id,
prefix: "public",
name: "post_links_destination_post_id_fkey",
type: :uuid
)
end
drop constraint(:comments, "comments_author_id_fkey")
drop constraint(:comments, "special_name_fkey")
alter table(:comments) do
modify :post_id,
references(:posts,
column: :id,
prefix: "public",
name: "special_name_fkey",
type: :uuid,
on_delete: :delete_all,
on_update: :update_all
)
end
alter table(:comments) do
modify :author_id,
references(:authors,
column: :id,
prefix: "public",
name: "comments_author_id_fkey",
type: :uuid
)
end
end
def down do
drop constraint(:comments, "comments_author_id_fkey")
alter table(:comments) do
modify :author_id,
references(:authors, column: :id, name: "comments_author_id_fkey", type: :uuid)
end
drop constraint(:comments, "special_name_fkey")
alter table(:comments) do
modify :post_id,
references(:posts,
column: :id,
name: "special_name_fkey",
type: :uuid,
on_delete: :delete_all,
on_update: :update_all
)
end
drop constraint(:post_links, "post_links_destination_post_id_fkey")
alter table(:post_links) do
modify :destination_post_id,
references(:posts,
column: :id,
name: "post_links_destination_post_id_fkey",
type: :uuid
)
end
drop constraint(:post_links, "post_links_source_post_id_fkey")
alter table(:post_links) do
modify :source_post_id,
references(:posts, column: :id, name: "post_links_source_post_id_fkey", type: :uuid)
end
alter table(:posts) do
remove :point
modify :decimal, :decimal, default: 0
end
drop constraint(:users, "users_org_id_fkey")
alter table(:users) do
modify :org_id,
references(:multitenant_orgs, column: :id, name: "users_org_id_fkey", type: :uuid)
end
end
end

View file

@ -0,0 +1,55 @@
defmodule AshPostgres.TestRepo.TenantMigrations.MigrateResources2 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
drop constraint(:multitenant_posts, "multitenant_posts_user_id_fkey")
drop constraint(:multitenant_posts, "multitenant_posts_org_id_fkey")
alter table(:multitenant_posts, prefix: prefix()) do
modify :org_id,
references(:multitenant_orgs,
column: :id,
prefix: "public",
name: "multitenant_posts_org_id_fkey",
type: :uuid
)
end
alter table(:multitenant_posts, prefix: prefix()) do
modify :user_id,
references(:users,
column: :id,
prefix: "public",
name: "multitenant_posts_user_id_fkey",
type: :uuid
)
end
end
def down do
drop constraint(:multitenant_posts, "multitenant_posts_user_id_fkey")
alter table(:multitenant_posts, prefix: prefix()) do
modify :user_id,
references(:users, column: :id, name: "multitenant_posts_user_id_fkey", type: :uuid)
end
drop constraint(:multitenant_posts, "multitenant_posts_org_id_fkey")
alter table(:multitenant_posts, prefix: prefix()) do
modify :org_id,
references(:multitenant_orgs,
column: :id,
name: "multitenant_posts_org_id_fkey",
type: :uuid
)
end
end
end

View file

@ -539,7 +539,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
end
end
describe "--check_migrated option" do
describe "--check option" do
setup do
defposts do
attributes do
@ -558,7 +558,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
AshPostgres.MigrationGenerator.generate(api,
snapshot_path: "test_snapshot_path",
migration_path: "test_migration_path",
check_generated: true
check: true
)
) == {:shutdown, 1}

View file

@ -57,6 +57,7 @@ defmodule AshPostgres.Test.Post do
attribute(:status, AshPostgres.Test.Types.Status)
attribute(:status_enum, AshPostgres.Test.Types.StatusEnum)
attribute(:status_enum_no_cast, AshPostgres.Test.Types.StatusEnumNoCast, source: :status_enum)
attribute(:point, AshPostgres.Test.Point)
create_timestamp(:created_at)
end

View file

@ -0,0 +1,34 @@
defmodule AshPostgres.Test.Point do
@moduledoc false
use Ash.Type
def storage_type, do: {:array, :float}
def cast_input(nil, _), do: {:ok, nil}
def cast_input({a, b, c}, _) when is_float(a) and is_float(b) and is_float(c) do
{:ok, {a, b, c}}
end
def cast_input(_, _), do: :error
def cast_stored(nil, _), do: {:ok, nil}
def cast_stored([a, b, c], _) when is_float(a) and is_float(b) and is_float(c) do
{:ok, {a, b, c}}
end
def cast_stored(_, _) do
:error
end
def dump_to_native(nil, _), do: {:ok, nil}
def dump_to_native({a, b, c}, _) when is_float(a) and is_float(b) and is_float(c) do
{:ok, [a, b, c]}
end
def dump_to_native(_, _) do
:error
end
end

30
test/type_test.exs Normal file
View file

@ -0,0 +1,30 @@
defmodule AshPostgres.Test.TypeTest do
use AshPostgres.RepoCase, async: false
alias AshPostgres.Test.{Api, Post}
require Ash.Query
test "complex custom types can be used" do
post =
Post
|> Ash.Changeset.new(%{title: "title", point: {1.0, 2.0, 3.0}})
|> Api.create!()
assert post.point == {1.0, 2.0, 3.0}
end
test "complex custom types can be accessed with fragments" do
Post
|> Ash.Changeset.new(%{title: "title", point: {1.0, 2.0, 3.0}})
|> Api.create!()
Post
|> Ash.Changeset.new(%{title: "title", point: {2.0, 1.0, 3.0}})
|> Api.create!()
assert [%{point: {2.0, 1.0, 3.0}}] =
Post
|> Ash.Query.filter(fragment("(?)[1] > (?)[2]", point, point))
|> Api.read!()
end
end