mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-19 13:03:14 +12:00
improvement!: 3.0 (#227)
* WIP * chore: fix mix.lock merge issues * improvement: upgrade to 3.0 * chore: remove `repo.to_tenant` * chore: continue removal of unnecessary helper * chore: use `Ash.ToTenant`
This commit is contained in:
parent
ec75b41dbe
commit
37cc01957d
112 changed files with 3581 additions and 3975 deletions
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,10 +1,9 @@
|
||||||
---
|
---
|
||||||
name: Proposal
|
name: Proposal
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: ""
|
||||||
labels: enhancement, needs review
|
labels: enhancement, needs review
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
@ -29,7 +28,7 @@ For example
|
||||||
Or
|
Or
|
||||||
|
|
||||||
```elixir
|
```elixir
|
||||||
Api.read(:resource, bar: 10) # <- Adding `bar` here would cause <x>
|
Ash.read(Resource, bar: 10) # <- Adding `bar` here would cause <x>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
|
|
2
.github/workflows/elixir.yml
vendored
2
.github/workflows/elixir.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
postgres-version: ["10", "12", "14", "16"]
|
postgres-version: ["14", "15", "16"]
|
||||||
uses: ash-project/ash/.github/workflows/ash-ci.yml@main
|
uses: ash-project/ash/.github/workflows/ash-ci.yml@main
|
||||||
with:
|
with:
|
||||||
postgres: true
|
postgres: true
|
||||||
|
|
3093
CHANGELOG.md
3093
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
@ -30,7 +30,7 @@ Then, configure each of your `Ash.Resource` resources by adding `use Ash.Resourc
|
||||||
|
|
||||||
```elixir
|
```elixir
|
||||||
defmodule MyApp.SomeResource do
|
defmodule MyApp.SomeResource do
|
||||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
use Ash.Resource, domain: MyDomain, data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
repo MyApp.Repo
|
repo MyApp.Repo
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.{Domain, Post}
|
||||||
|
|
||||||
ten_rows =
|
ten_rows =
|
||||||
1..10
|
1..10
|
||||||
|
@ -25,7 +25,7 @@ hundred_thousand_rows =
|
||||||
end)
|
end)
|
||||||
|
|
||||||
# do them both once to warm things up
|
# do them both once to warm things up
|
||||||
Api.bulk_create(ten_rows, Post, :create,
|
Ash.bulk_create(ten_rows, Post, :create,
|
||||||
batch_size: 10,
|
batch_size: 10,
|
||||||
max_concurrency: 2
|
max_concurrency: 2
|
||||||
)
|
)
|
||||||
|
@ -39,13 +39,13 @@ batch_size = 200
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"ash sync": fn input ->
|
"ash sync": fn input ->
|
||||||
%{error_count: 0} = Api.bulk_create(input, Post, :create,
|
%{error_count: 0} = Ash.bulk_create(input, Post, :create,
|
||||||
batch_size: batch_size,
|
batch_size: batch_size,
|
||||||
transaction: false
|
transaction: false
|
||||||
)
|
)
|
||||||
end,
|
end,
|
||||||
"ash sync assuming casted": fn input ->
|
"ash sync assuming casted": fn input ->
|
||||||
%{error_count: 0} = Api.bulk_create(input, Post, :create,
|
%{error_count: 0} = Ash.bulk_create(input, Post, :create,
|
||||||
batch_size: batch_size,
|
batch_size: batch_size,
|
||||||
transaction: false,
|
transaction: false,
|
||||||
assume_casted?: true
|
assume_casted?: true
|
||||||
|
@ -62,7 +62,7 @@ Benchee.run(
|
||||||
input
|
input
|
||||||
|> Stream.chunk_every(batch_size)
|
|> Stream.chunk_every(batch_size)
|
||||||
|> Task.async_stream(fn batch ->
|
|> Task.async_stream(fn batch ->
|
||||||
%{error_count: 0} = Api.bulk_create(batch, Post, :create,
|
%{error_count: 0} = Ash.bulk_create(batch, Post, :create,
|
||||||
transaction: false
|
transaction: false
|
||||||
)
|
)
|
||||||
end, max_concurrency: max_concurrency, timeout: :infinity)
|
end, max_concurrency: max_concurrency, timeout: :infinity)
|
||||||
|
@ -72,7 +72,7 @@ Benchee.run(
|
||||||
input
|
input
|
||||||
|> Stream.chunk_every(batch_size)
|
|> Stream.chunk_every(batch_size)
|
||||||
|> Task.async_stream(fn batch ->
|
|> Task.async_stream(fn batch ->
|
||||||
%{error_count: 0} = Api.bulk_create(batch, Post, :create,
|
%{error_count: 0} = Ash.bulk_create(batch, Post, :create,
|
||||||
transaction: false,
|
transaction: false,
|
||||||
assume_casted?: true
|
assume_casted?: true
|
||||||
)
|
)
|
||||||
|
@ -80,14 +80,14 @@ Benchee.run(
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end,
|
end,
|
||||||
"ash using own async option": fn input ->
|
"ash using own async option": fn input ->
|
||||||
%{error_count: 0} = Api.bulk_create(input, Post, :create,
|
%{error_count: 0} = Ash.bulk_create(input, Post, :create,
|
||||||
transaction: false,
|
transaction: false,
|
||||||
max_concurrency: max_concurrency,
|
max_concurrency: max_concurrency,
|
||||||
batch_size: batch_size
|
batch_size: batch_size
|
||||||
)
|
)
|
||||||
end,
|
end,
|
||||||
"ash using own async option assuming casted": fn input ->
|
"ash using own async option assuming casted": fn input ->
|
||||||
%{error_count: 0} = Api.bulk_create(input, Post, :create,
|
%{error_count: 0} = Ash.bulk_create(input, Post, :create,
|
||||||
transaction: false,
|
transaction: false,
|
||||||
assume_casted?: true,
|
assume_casted?: true,
|
||||||
max_concurrency: max_concurrency,
|
max_concurrency: max_concurrency,
|
||||||
|
|
|
@ -15,8 +15,8 @@ if Mix.env() == :dev do
|
||||||
end
|
end
|
||||||
|
|
||||||
if Mix.env() == :test do
|
if Mix.env() == :test do
|
||||||
config :ash, :validate_api_resource_inclusion?, false
|
config :ash, :validate_domain_resource_inclusion?, false
|
||||||
config :ash, :validate_api_config_inclusion?, false
|
config :ash, :validate_domain_config_inclusion?, false
|
||||||
|
|
||||||
config :ash_postgres, AshPostgres.TestRepo,
|
config :ash_postgres, AshPostgres.TestRepo,
|
||||||
username: "postgres",
|
username: "postgres",
|
||||||
|
@ -42,10 +42,10 @@ if Mix.env() == :test do
|
||||||
|
|
||||||
config :ash_postgres,
|
config :ash_postgres,
|
||||||
ecto_repos: [AshPostgres.TestRepo, AshPostgres.TestNoSandboxRepo],
|
ecto_repos: [AshPostgres.TestRepo, AshPostgres.TestNoSandboxRepo],
|
||||||
ash_apis: [
|
ash_domains: [
|
||||||
AshPostgres.Test.Api,
|
AshPostgres.Test.Domain,
|
||||||
AshPostgres.MultitenancyTest.Api,
|
AshPostgres.MultitenancyTest.Domain,
|
||||||
AshPostgres.Test.ComplexCalculations.Api
|
AshPostgres.Test.ComplexCalculations.Domain
|
||||||
]
|
]
|
||||||
|
|
||||||
config :logger, level: :warning
|
config :logger, level: :warning
|
||||||
|
|
|
@ -25,7 +25,7 @@ AshPostgres is built on top of ecto, so much of its behavior is pass-through/orc
|
||||||
For more information on generating migrations, see the module documentation here:
|
For more information on generating migrations, see the module documentation here:
|
||||||
`Mix.Tasks.AshPostgres.GenerateMigrations`, or run `mix help ash_postgres.generate_migrations`
|
`Mix.Tasks.AshPostgres.GenerateMigrations`, or run `mix help ash_postgres.generate_migrations`
|
||||||
|
|
||||||
For running your migrations, there is a mix task that will find all of the repos configured in your apis and run their
|
For running your migrations, there is a mix task that will find all of the repos configured in your domains and run their
|
||||||
migrations. It is a thin wrapper around `mix ecto.migrate`. Ours is called `mix ash_postgres.migrate`
|
migrations. It is a thin wrapper around `mix ecto.migrate`. Ours is called `mix ash_postgres.migrate`
|
||||||
|
|
||||||
If you want to run or rollback individual migrations, use the corresponding
|
If you want to run or rollback individual migrations, use the corresponding
|
||||||
|
@ -146,17 +146,17 @@ Tasks that need to be executed in the released application (because mix is not p
|
||||||
end
|
end
|
||||||
|
|
||||||
defp repos do
|
defp repos do
|
||||||
apis()
|
domains()
|
||||||
|> Enum.flat_map(fn api ->
|
|> Enum.flat_map(fn domain ->
|
||||||
api
|
domain
|
||||||
|> Ash.Api.Info.resources()
|
|> Ash.Domain.Info.resources()
|
||||||
|> Enum.map(&AshPostgres.DataLayer.Info.repo/1)
|
|> Enum.map(&AshPostgres.DataLayer.Info.repo/1)
|
||||||
end)
|
end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apis do
|
defp domains do
|
||||||
Application.fetch_env!(@app, :ash_apis)
|
Application.fetch_env!(@app, :ash_domains)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load_app do
|
defp load_app do
|
||||||
|
|
|
@ -5,6 +5,7 @@ To support leveraging the same resource backed by multiple tables (useful for th
|
||||||
```elixir
|
```elixir
|
||||||
defmodule MyApp.Reaction do
|
defmodule MyApp.Reaction do
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: MyDomain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -12,7 +13,7 @@ defmodule MyApp.Reaction do
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
attribute(:resource_id, :uuid)
|
attribute :resource_id, :uuid, public?: true
|
||||||
end
|
end
|
||||||
|
|
||||||
...
|
...
|
||||||
|
@ -24,6 +25,7 @@ Then, in your related resources, you set the table context like so:
|
||||||
```elixir
|
```elixir
|
||||||
defmodule MyApp.Post do
|
defmodule MyApp.Post do
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: MyDomain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
...
|
...
|
||||||
|
@ -37,6 +39,7 @@ end
|
||||||
|
|
||||||
defmodule MyApp.Comment do
|
defmodule MyApp.Comment do
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: MyDomain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
...
|
...
|
||||||
|
@ -61,6 +64,7 @@ For example:
|
||||||
|
|
||||||
```elixir
|
```elixir
|
||||||
defmodule MyApp.Reaction do
|
defmodule MyApp.Reaction do
|
||||||
|
# ...
|
||||||
actions do
|
actions do
|
||||||
read :for_comments do
|
read :for_comments do
|
||||||
prepare set_context(%{data_layer: %{table: "comment_reactions"}})
|
prepare set_context(%{data_layer: %{table: "comment_reactions"}})
|
||||||
|
|
|
@ -68,7 +68,7 @@ import Config
|
||||||
# This should already have been added in the first
|
# This should already have been added in the first
|
||||||
# getting started guide
|
# getting started guide
|
||||||
config :helpdesk,
|
config :helpdesk,
|
||||||
ash_apis: [Helpdesk.Support]
|
ash_domains: [Helpdesk.Support]
|
||||||
|
|
||||||
config :helpdesk,
|
config :helpdesk,
|
||||||
ecto_repos: [Helpdesk.Repo]
|
ecto_repos: [Helpdesk.Repo]
|
||||||
|
@ -155,6 +155,7 @@ Now we can add the data layer to our resources. The basic configuration for a re
|
||||||
# in lib/helpdesk/support/resources/ticket.ex
|
# in lib/helpdesk/support/resources/ticket.ex
|
||||||
|
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: Helpdesk.Support,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -167,6 +168,7 @@ Now we can add the data layer to our resources. The basic configuration for a re
|
||||||
# in lib/helpdesk/support/resources/representative.ex
|
# in lib/helpdesk/support/resources/representative.ex
|
||||||
|
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: Helpdesk.Support,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
|
110
lib/aggregate.ex
110
lib/aggregate.ex
|
@ -499,8 +499,7 @@ defmodule AshPostgres.Aggregate do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_aggregates_to_aggregates(resource, aggregates) do
|
defp resource_aggregates_to_aggregates(resource, aggregates) do
|
||||||
aggregates
|
Enum.reduce_while(aggregates, {:ok, []}, fn
|
||||||
|> Enum.reduce_while({:ok, []}, fn
|
|
||||||
%Ash.Query.Aggregate{} = aggregate, {:ok, aggregates} ->
|
%Ash.Query.Aggregate{} = aggregate, {:ok, aggregates} ->
|
||||||
{:cont, {:ok, [aggregate | aggregates]}}
|
{:cont, {:ok, [aggregate | aggregates]}}
|
||||||
|
|
||||||
|
@ -523,6 +522,8 @@ defmodule AshPostgres.Aggregate do
|
||||||
default: aggregate.default,
|
default: aggregate.default,
|
||||||
filterable?: aggregate.filterable?,
|
filterable?: aggregate.filterable?,
|
||||||
type: aggregate.type,
|
type: aggregate.type,
|
||||||
|
sortable?: aggregate.filterable?,
|
||||||
|
include_nil?: aggregate.include_nil?,
|
||||||
constraints: aggregate.constraints,
|
constraints: aggregate.constraints,
|
||||||
implementation: aggregate.implementation,
|
implementation: aggregate.implementation,
|
||||||
uniq?: aggregate.uniq?,
|
uniq?: aggregate.uniq?,
|
||||||
|
@ -685,11 +686,11 @@ defmodule AshPostgres.Aggregate do
|
||||||
type: type,
|
type: type,
|
||||||
constraints: constraints
|
constraints: constraints
|
||||||
} ->
|
} ->
|
||||||
{:ok, new_calc} = Ash.Query.Calculation.new(name, module, opts, {type, constraints})
|
{:ok, new_calc} = Ash.Query.Calculation.new(name, module, opts, type, constraints)
|
||||||
expression = module.expression(opts, aggregate.context)
|
expression = module.expression(opts, aggregate.context)
|
||||||
|
|
||||||
expression =
|
expression =
|
||||||
Ash.Filter.build_filter_from_template(
|
Ash.Expr.fill_template(
|
||||||
expression,
|
expression,
|
||||||
aggregate.context[:actor],
|
aggregate.context[:actor],
|
||||||
aggregate.context,
|
aggregate.context,
|
||||||
|
@ -719,11 +720,11 @@ defmodule AshPostgres.Aggregate do
|
||||||
expression = module.expression(opts, aggregate.context)
|
expression = module.expression(opts, aggregate.context)
|
||||||
|
|
||||||
expression =
|
expression =
|
||||||
Ash.Filter.build_filter_from_template(
|
Ash.Expr.fill_template(
|
||||||
expression,
|
expression,
|
||||||
aggregate.context[:actor],
|
aggregate.context.actor,
|
||||||
aggregate.context,
|
aggregate.context.arguments,
|
||||||
aggregate.context
|
aggregate.context.source_context
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, expression} =
|
{:ok, expression} =
|
||||||
|
@ -1189,6 +1190,13 @@ defmodule AshPostgres.Aggregate do
|
||||||
|
|
||||||
has_sort? = has_sort?(aggregate.query)
|
has_sort? = has_sort?(aggregate.query)
|
||||||
|
|
||||||
|
array_agg =
|
||||||
|
if AshPostgres.DataLayer.Info.pg_version_matches?(aggregate.resource, ">= 16.0.0") do
|
||||||
|
"any_value"
|
||||||
|
else
|
||||||
|
"array_agg"
|
||||||
|
end
|
||||||
|
|
||||||
{sorted, query} =
|
{sorted, query} =
|
||||||
if has_sort? || first_relationship.sort not in [nil, []] do
|
if has_sort? || first_relationship.sort not in [nil, []] do
|
||||||
{sort, binding} =
|
{sort, binding} =
|
||||||
|
@ -1211,31 +1219,63 @@ defmodule AshPostgres.Aggregate do
|
||||||
:return
|
:return
|
||||||
)
|
)
|
||||||
|
|
||||||
question_marks = Enum.map(sort_expr, fn _ -> " ? " end)
|
if aggregate.include_nil? do
|
||||||
|
question_marks = Enum.map(sort_expr, fn _ -> " ? " end)
|
||||||
|
|
||||||
{:ok, expr} =
|
{:ok, expr} =
|
||||||
AshPostgres.Functions.Fragment.casted_new(
|
Ash.Query.Function.Fragment.casted_new(
|
||||||
["array_agg(? ORDER BY #{question_marks})", field] ++ sort_expr
|
["#{array_agg}(? ORDER BY #{question_marks} FILTER (WHERE ? IS NOT NULL))", field] ++
|
||||||
)
|
sort_expr ++ [field]
|
||||||
|
)
|
||||||
|
|
||||||
{sort_expr, acc} =
|
{sort_expr, acc} =
|
||||||
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
AshPostgres.DataLayer.merge_expr_accumulator(query, acc)
|
AshPostgres.DataLayer.merge_expr_accumulator(query, acc)
|
||||||
|
|
||||||
{sort_expr, query}
|
{sort_expr, query}
|
||||||
|
else
|
||||||
|
question_marks = Enum.map(sort_expr, fn _ -> " ? " end)
|
||||||
|
|
||||||
|
{:ok, expr} =
|
||||||
|
Ash.Query.Function.Fragment.casted_new(
|
||||||
|
["#{array_agg}(? ORDER BY #{question_marks})", field] ++ sort_expr
|
||||||
|
)
|
||||||
|
|
||||||
|
{sort_expr, acc} =
|
||||||
|
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
||||||
|
|
||||||
|
query =
|
||||||
|
AshPostgres.DataLayer.merge_expr_accumulator(query, acc)
|
||||||
|
|
||||||
|
{sort_expr, query}
|
||||||
|
end
|
||||||
else
|
else
|
||||||
{Ecto.Query.dynamic(
|
case array_agg do
|
||||||
[row],
|
"array_agg" ->
|
||||||
fragment("array_agg(?)", ^field)
|
{Ecto.Query.dynamic(
|
||||||
), query}
|
[row],
|
||||||
|
fragment("array_agg(?)", ^field)
|
||||||
|
), query}
|
||||||
|
|
||||||
|
"any_value" ->
|
||||||
|
{Ecto.Query.dynamic(
|
||||||
|
[row],
|
||||||
|
fragment("any_value(?)", ^field)
|
||||||
|
), query}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{query, filtered} =
|
{query, filtered} =
|
||||||
filter_field(sorted, query, aggregate, relationship_path, is_single?)
|
filter_field(sorted, query, aggregate, relationship_path, is_single?)
|
||||||
|
|
||||||
value = Ecto.Query.dynamic(fragment("(?)[1]", ^filtered))
|
value =
|
||||||
|
if array_agg == "array_agg" do
|
||||||
|
Ecto.Query.dynamic(fragment("(?)[1]", ^filtered))
|
||||||
|
else
|
||||||
|
filtered
|
||||||
|
end
|
||||||
|
|
||||||
with_default =
|
with_default =
|
||||||
if aggregate.default_value do
|
if aggregate.default_value do
|
||||||
|
@ -1327,10 +1367,26 @@ defmodule AshPostgres.Aggregate do
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, expr} =
|
expr =
|
||||||
AshPostgres.Functions.Fragment.casted_new(
|
if aggregate.include_nil? do
|
||||||
["array_agg(#{distinct}? ORDER BY #{question_marks})", field] ++ sort_expr
|
{:ok, expr} =
|
||||||
)
|
Ash.Query.Function.Fragment.casted_new(
|
||||||
|
[
|
||||||
|
"array_agg(#{distinct}? ORDER BY #{question_marks} FILTER (WHERE ? IS NOT NULL))",
|
||||||
|
field
|
||||||
|
] ++
|
||||||
|
sort_expr ++ [field]
|
||||||
|
)
|
||||||
|
|
||||||
|
expr
|
||||||
|
else
|
||||||
|
{:ok, expr} =
|
||||||
|
Ash.Query.Function.Fragment.casted_new(
|
||||||
|
["array_agg(#{distinct}? ORDER BY #{question_marks})", field] ++ sort_expr
|
||||||
|
)
|
||||||
|
|
||||||
|
expr
|
||||||
|
end
|
||||||
|
|
||||||
{expr, acc} =
|
{expr, acc} =
|
||||||
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
||||||
|
|
|
@ -73,10 +73,11 @@ defmodule AshPostgres.Calculation do
|
||||||
expression =
|
expression =
|
||||||
Ash.Actions.Read.add_calc_context_to_filter(
|
Ash.Actions.Read.add_calc_context_to_filter(
|
||||||
expression,
|
expression,
|
||||||
calculation.context[:actor],
|
calculation.context.actor,
|
||||||
calculation.context[:authorize?],
|
calculation.context.authorize?,
|
||||||
calculation.context[:tenant],
|
calculation.context.tenant,
|
||||||
calculation.context[:tracer]
|
calculation.context.tracer,
|
||||||
|
nil
|
||||||
)
|
)
|
||||||
|
|
||||||
{expr, acc} =
|
{expr, acc} =
|
||||||
|
|
|
@ -384,11 +384,12 @@ defmodule AshPostgres.DataLayer do
|
||||||
|
|
||||||
use Spark.Dsl.Extension,
|
use Spark.Dsl.Extension,
|
||||||
sections: @sections,
|
sections: @sections,
|
||||||
transformers: [
|
verifiers: [
|
||||||
AshPostgres.Transformers.ValidateReferences,
|
AshPostgres.Verifiers.VerifyPostgresVersion,
|
||||||
AshPostgres.Transformers.EnsureTableOrPolymorphic,
|
AshPostgres.Verifiers.PreventMultidimensionalArrayAggregates,
|
||||||
AshPostgres.Transformers.PreventMultidimensionalArrayAggregates,
|
AshPostgres.Verifiers.ValidateReferences,
|
||||||
AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchType
|
AshPostgres.Verifiers.PreventAttributeMultitenancyAndNonFullMatchType,
|
||||||
|
AshPostgres.Verifiers.EnsureTableOrPolymorphic
|
||||||
]
|
]
|
||||||
|
|
||||||
def migrate(args) do
|
def migrate(args) do
|
||||||
|
@ -704,7 +705,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
repo = dynamic_repo(resource, query)
|
repo = dynamic_repo(resource, query)
|
||||||
|
|
||||||
with_savepoint(repo, query, fn ->
|
with_savepoint(repo, query, fn ->
|
||||||
{:ok, repo.all(query, repo_opts(nil, nil, resource)) |> remap_mapped_fields(query)}
|
{:ok, repo.all(query, repo_opts(repo, nil, nil, resource)) |> remap_mapped_fields(query)}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
|
@ -715,7 +716,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
defp no_table?(%{from: %{source: {"", _}}}), do: true
|
defp no_table?(%{from: %{source: {"", _}}}), do: true
|
||||||
defp no_table?(_), do: false
|
defp no_table?(_), do: false
|
||||||
|
|
||||||
defp repo_opts(timeout, nil, resource) do
|
defp repo_opts(_repo, timeout, nil, resource) do
|
||||||
if schema = AshPostgres.DataLayer.Info.schema(resource) do
|
if schema = AshPostgres.DataLayer.Info.schema(resource) do
|
||||||
[prefix: schema]
|
[prefix: schema]
|
||||||
else
|
else
|
||||||
|
@ -724,9 +725,9 @@ defmodule AshPostgres.DataLayer do
|
||||||
|> add_timeout(timeout)
|
|> add_timeout(timeout)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp repo_opts(timeout, tenant, resource) do
|
defp repo_opts(_repo, timeout, tenant, resource) do
|
||||||
if Ash.Resource.Info.multitenancy_strategy(resource) == :context do
|
if Ash.Resource.Info.multitenancy_strategy(resource) == :context do
|
||||||
[prefix: tenant]
|
[prefix: Ash.ToTenant.to_tenant(resource, tenant)]
|
||||||
else
|
else
|
||||||
if schema = AshPostgres.DataLayer.Info.schema(resource) do
|
if schema = AshPostgres.DataLayer.Info.schema(resource) do
|
||||||
[prefix: schema]
|
[prefix: schema]
|
||||||
|
@ -748,7 +749,6 @@ defmodule AshPostgres.DataLayer do
|
||||||
config = AshPostgres.DataLayer.Info.repo(resource, :mutate).config()
|
config = AshPostgres.DataLayer.Info.repo(resource, :mutate).config()
|
||||||
|
|
||||||
functions = [
|
functions = [
|
||||||
AshPostgres.Functions.Fragment,
|
|
||||||
AshPostgres.Functions.Like,
|
AshPostgres.Functions.Like,
|
||||||
AshPostgres.Functions.ILike
|
AshPostgres.Functions.ILike
|
||||||
]
|
]
|
||||||
|
@ -844,7 +844,8 @@ defmodule AshPostgres.DataLayer do
|
||||||
%{}
|
%{}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
dynamic_repo(resource, query).one(query, repo_opts(nil, nil, resource))
|
repo = dynamic_repo(resource, query)
|
||||||
|
repo.one(query, repo_opts(repo, nil, nil, resource))
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, add_single_aggs(result, resource, original_query, cant_group)}
|
{:ok, add_single_aggs(result, resource, original_query, cant_group)}
|
||||||
|
@ -880,10 +881,12 @@ defmodule AshPostgres.DataLayer do
|
||||||
|> Ecto.Query.select(%{})
|
|> Ecto.Query.select(%{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
repo = dynamic_repo(resource, filtered)
|
||||||
|
|
||||||
Map.put(
|
Map.put(
|
||||||
result || %{},
|
result || %{},
|
||||||
agg.name,
|
agg.name,
|
||||||
dynamic_repo(resource, filtered).exists?(filtered, repo_opts(nil, nil, resource))
|
repo.exists?(filtered, repo_opts(repo, nil, nil, resource))
|
||||||
)
|
)
|
||||||
|
|
||||||
agg, result ->
|
agg, result ->
|
||||||
|
@ -953,9 +956,11 @@ defmodule AshPostgres.DataLayer do
|
||||||
first_relationship
|
first_relationship
|
||||||
)
|
)
|
||||||
|
|
||||||
|
repo = dynamic_repo(resource, query)
|
||||||
|
|
||||||
Map.merge(
|
Map.merge(
|
||||||
result || %{},
|
result || %{},
|
||||||
dynamic_repo(resource, query).one(query, repo_opts(nil, nil, resource))
|
repo.one(query, repo_opts(repo, nil, nil, resource))
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -1049,9 +1054,11 @@ defmodule AshPostgres.DataLayer do
|
||||||
%{}
|
%{}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
dynamic_repo(source_resource, query).one(
|
repo = dynamic_repo(source_resource, query)
|
||||||
|
|
||||||
|
repo.one(
|
||||||
query,
|
query,
|
||||||
repo_opts(nil, nil, source_resource)
|
repo_opts(repo, nil, nil, source_resource)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1082,10 +1089,12 @@ defmodule AshPostgres.DataLayer do
|
||||||
|> elem(0)
|
|> elem(0)
|
||||||
|> Map.get(:resource)
|
|> Map.get(:resource)
|
||||||
|
|
||||||
|
repo = dynamic_repo(source_resource, lateral_join_query)
|
||||||
|
|
||||||
results =
|
results =
|
||||||
dynamic_repo(source_resource, lateral_join_query).all(
|
repo.all(
|
||||||
lateral_join_query,
|
lateral_join_query,
|
||||||
repo_opts(nil, nil, source_resource)
|
repo_opts(repo, nil, nil, source_resource)
|
||||||
)
|
)
|
||||||
|> remap_mapped_fields(query)
|
|> remap_mapped_fields(query)
|
||||||
|
|
||||||
|
@ -1357,7 +1366,8 @@ defmodule AshPostgres.DataLayer do
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def set_subquery_prefix(data_layer_query, source_query, resource) do
|
def set_subquery_prefix(data_layer_query, source_query, resource) do
|
||||||
config = AshPostgres.DataLayer.Info.repo(resource, :mutate).config()
|
repo = AshPostgres.DataLayer.Info.repo(resource, :mutate)
|
||||||
|
config = repo.config()
|
||||||
|
|
||||||
case data_layer_query do
|
case data_layer_query do
|
||||||
%{__ash_bindings__: %{context: %{data_layer: %{schema: schema}}}} when not is_nil(schema) ->
|
%{__ash_bindings__: %{context: %{data_layer: %{schema: schema}}}} when not is_nil(schema) ->
|
||||||
|
@ -1375,20 +1385,16 @@ defmodule AshPostgres.DataLayer do
|
||||||
%{
|
%{
|
||||||
data_layer_query
|
data_layer_query
|
||||||
| prefix:
|
| prefix:
|
||||||
to_string(
|
query_tenant || AshPostgres.DataLayer.Info.schema(resource) ||
|
||||||
query_tenant || AshPostgres.DataLayer.Info.schema(resource) ||
|
config[:default_prefix] ||
|
||||||
config[:default_prefix] ||
|
"public"
|
||||||
"public"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
%{
|
%{
|
||||||
data_layer_query
|
data_layer_query
|
||||||
| prefix:
|
| prefix:
|
||||||
to_string(
|
AshPostgres.DataLayer.Info.schema(resource) || config[:default_prefix] ||
|
||||||
AshPostgres.DataLayer.Info.schema(resource) || config[:default_prefix] ||
|
"public"
|
||||||
"public"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1418,7 +1424,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|> Map.update!(:__meta__, &Map.put(&1, :source, table(resource, changeset)))
|
|> Map.update!(:__meta__, &Map.put(&1, :source, table(resource, changeset)))
|
||||||
|> ecto_changeset(changeset, :update, true, true)
|
|> ecto_changeset(changeset, :update, true)
|
||||||
|
|
||||||
case bulk_updatable_query(query, resource, changeset.atomics, changeset.context) do
|
case bulk_updatable_query(query, resource, changeset.atomics, changeset.context) do
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
|
@ -1427,12 +1433,12 @@ defmodule AshPostgres.DataLayer do
|
||||||
{:ok, query} ->
|
{:ok, query} ->
|
||||||
try do
|
try do
|
||||||
repo = dynamic_repo(resource, changeset)
|
repo = dynamic_repo(resource, changeset)
|
||||||
repo_opts = repo_opts(changeset.timeout, changeset.tenant, changeset.resource)
|
repo_opts = repo_opts(repo, changeset.timeout, changeset.tenant, changeset.resource)
|
||||||
|
|
||||||
case query_with_atomics(
|
case query_with_atomics(
|
||||||
resource,
|
resource,
|
||||||
query,
|
query,
|
||||||
ecto_changeset.filters,
|
changeset.filter,
|
||||||
changeset.atomics,
|
changeset.atomics,
|
||||||
ecto_changeset.changes,
|
ecto_changeset.changes,
|
||||||
[]
|
[]
|
||||||
|
@ -1582,7 +1588,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|> Map.update!(:__meta__, &Map.put(&1, :source, table(resource, changeset)))
|
|> Map.update!(:__meta__, &Map.put(&1, :source, table(resource, changeset)))
|
||||||
|> ecto_changeset(changeset, :update, true, true)
|
|> ecto_changeset(changeset, :update, true)
|
||||||
|
|
||||||
try do
|
try do
|
||||||
query =
|
query =
|
||||||
|
@ -1599,10 +1605,10 @@ defmodule AshPostgres.DataLayer do
|
||||||
end
|
end
|
||||||
|> Ecto.Query.exclude(:order_by)
|
|> Ecto.Query.exclude(:order_by)
|
||||||
|
|
||||||
repo_opts = repo_opts(changeset.timeout, changeset.tenant, changeset.resource)
|
|
||||||
|
|
||||||
repo = dynamic_repo(resource, changeset)
|
repo = dynamic_repo(resource, changeset)
|
||||||
|
|
||||||
|
repo_opts = repo_opts(repo, changeset.timeout, changeset.tenant, changeset.resource)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
if Enum.any?(query.joins, &(&1.qual != :inner)) do
|
if Enum.any?(query.joins, &(&1.qual != :inner)) do
|
||||||
root_query =
|
root_query =
|
||||||
|
@ -1663,7 +1669,11 @@ defmodule AshPostgres.DataLayer do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def bulk_create(resource, stream, options) do
|
def bulk_create(resource, stream, options) do
|
||||||
opts = repo_opts(nil, options[:tenant], resource)
|
changesets = Enum.to_list(stream)
|
||||||
|
|
||||||
|
repo = dynamic_repo(resource, Enum.at(changesets, 0))
|
||||||
|
|
||||||
|
opts = repo_opts(repo, nil, options[:tenant], resource)
|
||||||
|
|
||||||
opts =
|
opts =
|
||||||
if options.return_records? do
|
if options.return_records? do
|
||||||
|
@ -1672,8 +1682,6 @@ defmodule AshPostgres.DataLayer do
|
||||||
opts
|
opts
|
||||||
end
|
end
|
||||||
|
|
||||||
changesets = Enum.to_list(stream)
|
|
||||||
repo = dynamic_repo(resource, Enum.at(changesets, 0))
|
|
||||||
source = resolve_source(resource, Enum.at(changesets, 0))
|
source = resolve_source(resource, Enum.at(changesets, 0))
|
||||||
|
|
||||||
try do
|
try do
|
||||||
|
@ -1681,7 +1689,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
if options[:upsert?] do
|
if options[:upsert?] do
|
||||||
# Ash groups changesets by atomics before dispatching them to the data layer
|
# Ash groups changesets by atomics before dispatching them to the data layer
|
||||||
# this means that all changesets have the same atomics
|
# this means that all changesets have the same atomics
|
||||||
%{atomics: atomics, filters: filters} = Enum.at(changesets, 0)
|
%{atomics: atomics, filter: filter} = Enum.at(changesets, 0)
|
||||||
|
|
||||||
query = from(row in source, as: ^0)
|
query = from(row in source, as: ^0)
|
||||||
|
|
||||||
|
@ -1696,7 +1704,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
case query_with_atomics(
|
case query_with_atomics(
|
||||||
resource,
|
resource,
|
||||||
query,
|
query,
|
||||||
filters,
|
filter,
|
||||||
atomics,
|
atomics,
|
||||||
%{},
|
%{},
|
||||||
upsert_set
|
upsert_set
|
||||||
|
@ -1967,10 +1975,6 @@ defmodule AshPostgres.DataLayer do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_errors({:error, %Ecto.Changeset{errors: errors}}) do
|
|
||||||
{:error, Enum.map(errors, &to_ash_error/1)}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp to_ash_error({field, {message, vars}}) do
|
defp to_ash_error({field, {message, vars}}) do
|
||||||
Ash.Error.Changes.InvalidAttribute.exception(
|
Ash.Error.Changes.InvalidAttribute.exception(
|
||||||
field: field,
|
field: field,
|
||||||
|
@ -1979,25 +1983,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ecto_changeset(record, changeset, type, table_error? \\ true, bulk_update? \\ false) do
|
defp ecto_changeset(record, changeset, type, table_error? \\ true) do
|
||||||
filters =
|
|
||||||
if changeset.action_type == :create do
|
|
||||||
%{}
|
|
||||||
else
|
|
||||||
Map.get(changeset, :filters, %{})
|
|
||||||
end
|
|
||||||
|
|
||||||
filters =
|
|
||||||
if changeset.action_type == :create || bulk_update? do
|
|
||||||
filters
|
|
||||||
else
|
|
||||||
changeset.resource
|
|
||||||
|> Ash.Resource.Info.primary_key()
|
|
||||||
|> Enum.reduce(filters, fn key, filters ->
|
|
||||||
Map.put(filters, key, Map.get(record, key))
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
attributes =
|
attributes =
|
||||||
changeset.resource
|
changeset.resource
|
||||||
|> Ash.Resource.Info.attributes()
|
|> Ash.Resource.Info.attributes()
|
||||||
|
@ -2013,7 +1999,6 @@ defmodule AshPostgres.DataLayer do
|
||||||
|> to_ecto()
|
|> to_ecto()
|
||||||
|> set_table(changeset, type, table_error?)
|
|> set_table(changeset, type, table_error?)
|
||||||
|> Ecto.Changeset.change(Map.take(changeset.attributes, attributes_to_change))
|
|> Ecto.Changeset.change(Map.take(changeset.attributes, attributes_to_change))
|
||||||
|> Map.update!(:filters, &Map.merge(&1, filters))
|
|
||||||
|> add_configured_foreign_key_constraints(record.__struct__)
|
|> add_configured_foreign_key_constraints(record.__struct__)
|
||||||
|> add_unique_indexes(record.__struct__, changeset)
|
|> add_unique_indexes(record.__struct__, changeset)
|
||||||
|> add_check_constraints(record.__struct__)
|
|> add_check_constraints(record.__struct__)
|
||||||
|
@ -2615,7 +2600,11 @@ defmodule AshPostgres.DataLayer do
|
||||||
|> ecto_changeset(changeset, :update)
|
|> ecto_changeset(changeset, :update)
|
||||||
|
|
||||||
source = resolve_source(resource, changeset)
|
source = resolve_source(resource, changeset)
|
||||||
query = from(row in source, as: ^0) |> default_bindings(resource, changeset.context)
|
|
||||||
|
query =
|
||||||
|
from(row in source, as: ^0)
|
||||||
|
|> default_bindings(resource, changeset.context)
|
||||||
|
|> pkey_filter(changeset.data)
|
||||||
|
|
||||||
select = Keyword.keys(changeset.atomics) ++ Ash.Resource.Info.primary_key(resource)
|
select = Keyword.keys(changeset.atomics) ++ Ash.Resource.Info.primary_key(resource)
|
||||||
|
|
||||||
|
@ -2630,7 +2619,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
case query_with_atomics(
|
case query_with_atomics(
|
||||||
resource,
|
resource,
|
||||||
query,
|
query,
|
||||||
ecto_changeset.filters,
|
changeset.filter,
|
||||||
changeset.atomics,
|
changeset.atomics,
|
||||||
ecto_changeset.changes,
|
ecto_changeset.changes,
|
||||||
[]
|
[]
|
||||||
|
@ -2639,13 +2628,12 @@ defmodule AshPostgres.DataLayer do
|
||||||
{:ok, changeset.data}
|
{:ok, changeset.data}
|
||||||
|
|
||||||
{:ok, query} ->
|
{:ok, query} ->
|
||||||
repo_opts = repo_opts(changeset.timeout, changeset.tenant, changeset.resource)
|
repo = dynamic_repo(resource, changeset)
|
||||||
|
repo_opts = repo_opts(repo, changeset.timeout, changeset.tenant, changeset.resource)
|
||||||
|
|
||||||
repo_opts =
|
repo_opts =
|
||||||
Keyword.put(repo_opts, :returning, Keyword.keys(changeset.atomics))
|
Keyword.put(repo_opts, :returning, Keyword.keys(changeset.atomics))
|
||||||
|
|
||||||
repo = dynamic_repo(resource, changeset)
|
|
||||||
|
|
||||||
result =
|
result =
|
||||||
with_savepoint(repo, query, fn ->
|
with_savepoint(repo, query, fn ->
|
||||||
repo.update_all(
|
repo.update_all(
|
||||||
|
@ -2660,7 +2648,7 @@ defmodule AshPostgres.DataLayer do
|
||||||
{:error,
|
{:error,
|
||||||
Ash.Error.Changes.StaleRecord.exception(
|
Ash.Error.Changes.StaleRecord.exception(
|
||||||
resource: resource,
|
resource: resource,
|
||||||
filters: ecto_changeset.filters
|
filters: changeset.filter
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{1, [result]} ->
|
{1, [result]} ->
|
||||||
|
@ -2684,20 +2672,29 @@ defmodule AshPostgres.DataLayer do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp pkey_filter(query, %resource{} = record) do
|
||||||
|
pkey =
|
||||||
|
record
|
||||||
|
|> Map.take(Ash.Resource.Info.primary_key(resource))
|
||||||
|
|> Map.to_list()
|
||||||
|
|
||||||
|
Ecto.Query.where(query, ^pkey)
|
||||||
|
end
|
||||||
|
|
||||||
defp query_with_atomics(
|
defp query_with_atomics(
|
||||||
resource,
|
resource,
|
||||||
query,
|
query,
|
||||||
filters,
|
filter,
|
||||||
atomics,
|
atomics,
|
||||||
updating_one_changes,
|
updating_one_changes,
|
||||||
existing_set
|
existing_set
|
||||||
) do
|
) do
|
||||||
query =
|
query =
|
||||||
Enum.reduce(filters, query, fn {key, value}, query ->
|
if is_nil(filter) do
|
||||||
from(row in query,
|
query
|
||||||
where: field(row, ^key) == ^value
|
else
|
||||||
)
|
filter(query, filter, resource)
|
||||||
end)
|
end
|
||||||
|
|
||||||
atomics_result =
|
atomics_result =
|
||||||
Enum.reduce_while(atomics, {:ok, query, []}, fn {field, expr}, {:ok, query, set} ->
|
Enum.reduce_while(atomics, {:ok, query, []}, fn {field, expr}, {:ok, query, set} ->
|
||||||
|
@ -2792,20 +2789,23 @@ defmodule AshPostgres.DataLayer do
|
||||||
try do
|
try do
|
||||||
repo = dynamic_repo(resource, changeset)
|
repo = dynamic_repo(resource, changeset)
|
||||||
|
|
||||||
result =
|
source = resolve_source(resource, changeset)
|
||||||
repo.delete(
|
|
||||||
ecto_changeset,
|
|
||||||
repo_opts(changeset.timeout, changeset.tenant, changeset.resource)
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
from(row in source, as: ^0)
|
||||||
|> from_ecto()
|
|> default_bindings(resource, changeset.context)
|
||||||
|
|> filter(changeset.filter, resource)
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, _record} ->
|
{:ok, query} ->
|
||||||
|
query
|
||||||
|
|> pkey_filter(record)
|
||||||
|
|> repo.delete_all(
|
||||||
|
repo_opts(repo, changeset.timeout, changeset.tenant, changeset.resource)
|
||||||
|
)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
handle_errors({:error, error})
|
{:error, error}
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
e ->
|
e ->
|
||||||
|
@ -3201,9 +3201,13 @@ defmodule AshPostgres.DataLayer do
|
||||||
|> Enum.reduce(query, fn filter, query ->
|
|> Enum.reduce(query, fn filter, query ->
|
||||||
{dynamic, acc} = AshPostgres.Expr.dynamic_expr(query, filter, query.__ash_bindings__)
|
{dynamic, acc} = AshPostgres.Expr.dynamic_expr(query, filter, query.__ash_bindings__)
|
||||||
|
|
||||||
query
|
if is_nil(dynamic) do
|
||||||
|> Ecto.Query.where(^dynamic)
|
query
|
||||||
|> merge_expr_accumulator(acc)
|
else
|
||||||
|
query
|
||||||
|
|> Ecto.Query.where([], ^dynamic)
|
||||||
|
|> merge_expr_accumulator(acc)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,21 @@ defmodule AshPostgres.DataLayer.Info do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Checks a version requirement against the resource's repo's postgres version"
|
||||||
|
def pg_version_matches?(resource, requirement) do
|
||||||
|
resource
|
||||||
|
|> pg_version()
|
||||||
|
|> Version.match?(requirement)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Gets the resource's repo's postgres version"
|
||||||
|
def pg_version(resource) do
|
||||||
|
case repo(resource, :read).pg_version() do
|
||||||
|
%Version{} = version -> version
|
||||||
|
string when is_binary(string) -> Version.parse!(string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "The configured table for a resource"
|
@doc "The configured table for a resource"
|
||||||
def table(resource) do
|
def table(resource) do
|
||||||
Extension.get_opt(resource, [:postgres], :table, nil, true)
|
Extension.get_opt(resource, [:postgres], :table, nil, true)
|
||||||
|
|
49
lib/expr.ex
49
lib/expr.ex
|
@ -14,6 +14,7 @@ defmodule AshPostgres.Expr do
|
||||||
DateAdd,
|
DateAdd,
|
||||||
DateTimeAdd,
|
DateTimeAdd,
|
||||||
Error,
|
Error,
|
||||||
|
Fragment,
|
||||||
FromNow,
|
FromNow,
|
||||||
GetPath,
|
GetPath,
|
||||||
If,
|
If,
|
||||||
|
@ -21,6 +22,7 @@ defmodule AshPostgres.Expr do
|
||||||
Length,
|
Length,
|
||||||
Now,
|
Now,
|
||||||
Round,
|
Round,
|
||||||
|
StringDowncase,
|
||||||
StringJoin,
|
StringJoin,
|
||||||
StringLength,
|
StringLength,
|
||||||
StringSplit,
|
StringSplit,
|
||||||
|
@ -29,7 +31,7 @@ defmodule AshPostgres.Expr do
|
||||||
Type
|
Type
|
||||||
}
|
}
|
||||||
|
|
||||||
alias AshPostgres.Functions.{Fragment, ILike, Like, TrigramSimilarity, VectorCosineDistance}
|
alias AshPostgres.Functions.{ILike, Like, TrigramSimilarity, VectorCosineDistance}
|
||||||
|
|
||||||
require Ecto.Query
|
require Ecto.Query
|
||||||
|
|
||||||
|
@ -725,6 +727,27 @@ defmodule AshPostgres.Expr do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_dynamic_expr(
|
||||||
|
query,
|
||||||
|
%StringDowncase{arguments: [value], embedded?: pred_embedded?},
|
||||||
|
bindings,
|
||||||
|
embedded?,
|
||||||
|
acc,
|
||||||
|
type
|
||||||
|
) do
|
||||||
|
do_dynamic_expr(
|
||||||
|
query,
|
||||||
|
%Fragment{
|
||||||
|
embedded?: pred_embedded?,
|
||||||
|
arguments: [raw: "lower(", expr: value, raw: ")"]
|
||||||
|
},
|
||||||
|
bindings,
|
||||||
|
embedded?,
|
||||||
|
acc,
|
||||||
|
type
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp do_dynamic_expr(
|
defp do_dynamic_expr(
|
||||||
query,
|
query,
|
||||||
%StringTrim{arguments: [value], embedded?: pred_embedded?},
|
%StringTrim{arguments: [value], embedded?: pred_embedded?},
|
||||||
|
@ -1074,10 +1097,11 @@ defmodule AshPostgres.Expr do
|
||||||
expression =
|
expression =
|
||||||
Ash.Actions.Read.add_calc_context_to_filter(
|
Ash.Actions.Read.add_calc_context_to_filter(
|
||||||
expression,
|
expression,
|
||||||
calculation.context[:actor],
|
calculation.context.actor,
|
||||||
calculation.context[:authorize?],
|
calculation.context.authorize?,
|
||||||
calculation.context[:tenant],
|
calculation.context.tenant,
|
||||||
calculation.context[:tracer]
|
calculation.context.tracer,
|
||||||
|
nil
|
||||||
)
|
)
|
||||||
|
|
||||||
do_dynamic_expr(
|
do_dynamic_expr(
|
||||||
|
@ -1190,7 +1214,8 @@ defmodule AshPostgres.Expr do
|
||||||
aggregate.context[:actor],
|
aggregate.context[:actor],
|
||||||
aggregate.context[:authorize?],
|
aggregate.context[:authorize?],
|
||||||
aggregate.context[:tenant],
|
aggregate.context[:tenant],
|
||||||
aggregate.context[:tracer]
|
aggregate.context[:tracer],
|
||||||
|
nil
|
||||||
)
|
)
|
||||||
|
|
||||||
{value, acc} = do_dynamic_expr(query, ref, query.__ash_bindings__, false, acc)
|
{value, acc} = do_dynamic_expr(query, ref, query.__ash_bindings__, false, acc)
|
||||||
|
@ -1452,10 +1477,10 @@ defmodule AshPostgres.Expr do
|
||||||
end
|
end
|
||||||
|
|
||||||
{encoded, acc} =
|
{encoded, acc} =
|
||||||
if Ash.Filter.TemplateHelpers.expr?(input) do
|
if Ash.Expr.expr?(input) do
|
||||||
frag_parts =
|
frag_parts =
|
||||||
Enum.flat_map(input, fn {key, value} ->
|
Enum.flat_map(input, fn {key, value} ->
|
||||||
if Ash.Filter.TemplateHelpers.expr?(value) do
|
if Ash.Expr.expr?(value) do
|
||||||
[
|
[
|
||||||
expr: to_string(key),
|
expr: to_string(key),
|
||||||
raw: "::text, ",
|
raw: "::text, ",
|
||||||
|
@ -1720,7 +1745,7 @@ defmodule AshPostgres.Expr do
|
||||||
when is_map(value) and not is_struct(value) do
|
when is_map(value) and not is_struct(value) do
|
||||||
if bindings[:location] == :update &&
|
if bindings[:location] == :update &&
|
||||||
Enum.any?(value, fn {key, value} ->
|
Enum.any?(value, fn {key, value} ->
|
||||||
Ash.Filter.TemplateHelpers.expr?(key) || Ash.Filter.TemplateHelpers.expr?(value)
|
Ash.Expr.expr?(key) || Ash.Expr.expr?(value)
|
||||||
end) do
|
end) do
|
||||||
elements =
|
elements =
|
||||||
value
|
value
|
||||||
|
@ -1770,7 +1795,7 @@ defmodule AshPostgres.Expr do
|
||||||
if other && is_atom(other) && !is_boolean(other) do
|
if other && is_atom(other) && !is_boolean(other) do
|
||||||
{to_string(other), acc}
|
{to_string(other), acc}
|
||||||
else
|
else
|
||||||
if Ash.Filter.TemplateHelpers.expr?(other) do
|
if Ash.Expr.expr?(other) do
|
||||||
if is_list(other) do
|
if is_list(other) do
|
||||||
list_expr(query, other, bindings, true, acc, type)
|
list_expr(query, other, bindings, true, acc, type)
|
||||||
else
|
else
|
||||||
|
@ -1801,7 +1826,7 @@ defmodule AshPostgres.Expr do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_dynamic_expr(query, value, bindings, false, acc, type) do
|
defp do_dynamic_expr(query, value, bindings, false, acc, type) do
|
||||||
if Ash.Filter.TemplateHelpers.expr?(value) do
|
if Ash.Expr.expr?(value) do
|
||||||
if is_list(value) do
|
if is_list(value) do
|
||||||
list_expr(query, value, bindings, false, acc, type)
|
list_expr(query, value, bindings, false, acc, type)
|
||||||
else
|
else
|
||||||
|
@ -2033,7 +2058,7 @@ defmodule AshPostgres.Expr do
|
||||||
defp list_expr(query, value, bindings, embedded?, acc, type) do
|
defp list_expr(query, value, bindings, embedded?, acc, type) do
|
||||||
if !Enum.empty?(value) &&
|
if !Enum.empty?(value) &&
|
||||||
Enum.any?(value, fn value ->
|
Enum.any?(value, fn value ->
|
||||||
Ash.Filter.TemplateHelpers.expr?(value) || is_map(value) || is_list(value)
|
Ash.Expr.expr?(value) || is_map(value) || is_list(value)
|
||||||
end) do
|
end) do
|
||||||
type =
|
type =
|
||||||
case type do
|
case type do
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
defmodule AshPostgres.Functions.Fragment do
|
|
||||||
@moduledoc """
|
|
||||||
A function that maps to ecto's `fragment` function
|
|
||||||
|
|
||||||
https://hexdocs.pm/ecto/Ecto.Query.API.html#fragment/1
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Ash.Query.Function, name: :fragment
|
|
||||||
|
|
||||||
def private?, do: true
|
|
||||||
|
|
||||||
# Varargs is special, and should only be used in rare circumstances (like this one)
|
|
||||||
# no type casting or help can be provided for these functions.
|
|
||||||
def args, do: :var_args
|
|
||||||
|
|
||||||
def new([fragment | _]) when not is_binary(fragment) do
|
|
||||||
{:error, "First argument to `fragment` must be a string."}
|
|
||||||
end
|
|
||||||
|
|
||||||
def new([fragment | rest]) do
|
|
||||||
split = split_fragment(fragment)
|
|
||||||
|
|
||||||
if Enum.count(split, &(&1 == :slot)) != length(rest) do
|
|
||||||
{:error,
|
|
||||||
"fragment(...) expects extra arguments in the same amount of question marks in string. " <>
|
|
||||||
"It received #{Enum.count(split, &(&1 == :slot))} extra argument(s) but expected #{length(rest)}"}
|
|
||||||
else
|
|
||||||
{:ok, %__MODULE__{arguments: merge_fragment(split, rest)}}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def casted_new([fragment | _]) when not is_binary(fragment) do
|
|
||||||
{:error, "First argument to `fragment` must be a string."}
|
|
||||||
end
|
|
||||||
|
|
||||||
def casted_new([fragment | rest]) do
|
|
||||||
split = split_fragment(fragment)
|
|
||||||
|
|
||||||
if Enum.count(split, &(&1 == :slot)) != length(rest) do
|
|
||||||
{:error,
|
|
||||||
"fragment(...) expects extra arguments in the same amount of question marks in string. " <>
|
|
||||||
"It received #{Enum.count(split, &(&1 == :slot))} extra argument(s) but expected #{length(rest)}"}
|
|
||||||
else
|
|
||||||
{:ok, %__MODULE__{arguments: merge_fragment(split, rest, :casted_expr)}}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp merge_fragment(expr, args, tag \\ :expr)
|
|
||||||
defp merge_fragment([], [], _tag), do: []
|
|
||||||
|
|
||||||
defp merge_fragment([:slot | rest], [arg | rest_args], tag) do
|
|
||||||
[{tag, arg} | merge_fragment(rest, rest_args, tag)]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp merge_fragment([val | rest], rest_args, tag) do
|
|
||||||
[{:raw, val} | merge_fragment(rest, rest_args, tag)]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp split_fragment(frag, consumed \\ "")
|
|
||||||
|
|
||||||
defp split_fragment(<<>>, consumed),
|
|
||||||
do: [consumed]
|
|
||||||
|
|
||||||
defp split_fragment(<<??, rest::binary>>, consumed),
|
|
||||||
do: [consumed, :slot | split_fragment(rest, "")]
|
|
||||||
|
|
||||||
defp split_fragment(<<?\\, ??, rest::binary>>, consumed),
|
|
||||||
do: split_fragment(rest, consumed <> <<??>>)
|
|
||||||
|
|
||||||
defp split_fragment(<<first::utf8, rest::binary>>, consumed),
|
|
||||||
do: split_fragment(rest, consumed <> <<first::utf8>>)
|
|
||||||
end
|
|
|
@ -20,11 +20,11 @@ defmodule AshPostgres.MigrationGenerator do
|
||||||
check: false,
|
check: false,
|
||||||
drop_columns: false
|
drop_columns: false
|
||||||
|
|
||||||
def generate(apis, opts \\ []) do
|
def generate(domains, opts \\ []) do
|
||||||
apis = List.wrap(apis)
|
domains = List.wrap(domains)
|
||||||
opts = opts(opts)
|
opts = opts(opts)
|
||||||
|
|
||||||
all_resources = Enum.uniq(Enum.flat_map(apis, &Ash.Api.Info.resources/1))
|
all_resources = Enum.uniq(Enum.flat_map(domains, &Ash.Domain.Info.resources/1))
|
||||||
|
|
||||||
{tenant_snapshots, snapshots} =
|
{tenant_snapshots, snapshots} =
|
||||||
all_resources
|
all_resources
|
||||||
|
@ -60,8 +60,8 @@ defmodule AshPostgres.MigrationGenerator do
|
||||||
|
|
||||||
Does not support everything supported by the migration generator.
|
Does not support everything supported by the migration generator.
|
||||||
"""
|
"""
|
||||||
def take_snapshots(api, repo, only_resources \\ nil) do
|
def take_snapshots(domain, repo, only_resources \\ nil) do
|
||||||
all_resources = api |> Ash.Api.Info.resources() |> Enum.uniq()
|
all_resources = domain |> Ash.Domain.Info.resources() |> Enum.uniq()
|
||||||
|
|
||||||
all_resources
|
all_resources
|
||||||
|> Enum.filter(fn resource ->
|
|> Enum.filter(fn resource ->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.MixHelpers do
|
defmodule AshPostgres.MixHelpers do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
def apis!(opts, args) do
|
def domains!(opts, args) do
|
||||||
apps =
|
apps =
|
||||||
if apps_paths = Mix.Project.apps_paths() do
|
if apps_paths = Mix.Project.apps_paths() do
|
||||||
apps_paths |> Map.keys() |> Enum.sort()
|
apps_paths |> Map.keys() |> Enum.sort()
|
||||||
|
@ -8,46 +8,46 @@ defmodule AshPostgres.MixHelpers do
|
||||||
[Mix.Project.config()[:app]]
|
[Mix.Project.config()[:app]]
|
||||||
end
|
end
|
||||||
|
|
||||||
configured_apis = Enum.flat_map(apps, &Application.get_env(&1, :ash_apis, []))
|
configure_domains = Enum.flat_map(apps, &Application.get_env(&1, :ash_domains, []))
|
||||||
|
|
||||||
apis =
|
domains =
|
||||||
if opts[:apis] && opts[:apis] != "" do
|
if opts[:domains] && opts[:domains] != "" do
|
||||||
opts[:apis]
|
opts[:domains]
|
||||||
|> Kernel.||("")
|
|> Kernel.||("")
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|> Enum.flat_map(fn
|
|> Enum.flat_map(fn
|
||||||
"" ->
|
"" ->
|
||||||
[]
|
[]
|
||||||
|
|
||||||
api ->
|
domain ->
|
||||||
[Module.concat([api])]
|
[Module.concat([domain])]
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
configured_apis
|
configure_domains
|
||||||
end
|
end
|
||||||
|
|
||||||
apis
|
domains
|
||||||
|> Enum.map(&ensure_compiled(&1, args))
|
|> Enum.map(&ensure_compiled(&1, args))
|
||||||
|> case do
|
|> case do
|
||||||
[] ->
|
[] ->
|
||||||
raise "must supply the --apis argument, or set `config :my_app, ash_apis: [...]` in config"
|
raise "must supply the --domains argument, or set `config :my_app, ash_domains: [...]` in config"
|
||||||
|
|
||||||
apis ->
|
domains ->
|
||||||
apis
|
domains
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def repos!(opts, args) do
|
def repos!(opts, args) do
|
||||||
apis = apis!(opts, args)
|
domains = domains!(opts, args)
|
||||||
|
|
||||||
resources =
|
resources =
|
||||||
apis
|
domains
|
||||||
|> Enum.flat_map(&Ash.Api.Info.resources/1)
|
|> Enum.flat_map(&Ash.Domain.Info.resources/1)
|
||||||
|> Enum.filter(&(Ash.DataLayer.data_layer(&1) == AshPostgres.DataLayer))
|
|> Enum.filter(&(Ash.DataLayer.data_layer(&1) == AshPostgres.DataLayer))
|
||||||
|> case do
|
|> case do
|
||||||
[] ->
|
[] ->
|
||||||
raise """
|
raise """
|
||||||
No resources with `data_layer: AshPostgres.DataLayer` found in the apis #{Enum.map_join(apis, ",", &inspect/1)}.
|
No resources with `data_layer: AshPostgres.DataLayer` found in the domains #{Enum.map_join(domains, ",", &inspect/1)}.
|
||||||
|
|
||||||
Must be able to find at least one resource with `data_layer: AshPostgres.DataLayer`.
|
Must be able to find at least one resource with `data_layer: AshPostgres.DataLayer`.
|
||||||
"""
|
"""
|
||||||
|
@ -64,7 +64,7 @@ defmodule AshPostgres.MixHelpers do
|
||||||
|> case do
|
|> case do
|
||||||
[] ->
|
[] ->
|
||||||
raise """
|
raise """
|
||||||
No repos could be found configured on the resources in the apis: #{Enum.map_join(apis, ",", &inspect/1)}
|
No repos could be found configured on the resources in the domains: #{Enum.map_join(domains, ",", &inspect/1)}
|
||||||
|
|
||||||
At least one resource must have a repo configured.
|
At least one resource must have a repo configured.
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ defmodule AshPostgres.MixHelpers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_compiled(api, args) do
|
defp ensure_compiled(domain, args) do
|
||||||
if Code.ensure_loaded?(Mix.Tasks.App.Config) do
|
if Code.ensure_loaded?(Mix.Tasks.App.Config) do
|
||||||
Mix.Task.run("app.config", args)
|
Mix.Task.run("app.config", args)
|
||||||
else
|
else
|
||||||
|
@ -106,18 +106,18 @@ defmodule AshPostgres.MixHelpers do
|
||||||
"--no-compile" not in args && Mix.Task.run("compile", args)
|
"--no-compile" not in args && Mix.Task.run("compile", args)
|
||||||
end
|
end
|
||||||
|
|
||||||
case Code.ensure_compiled(api) do
|
case Code.ensure_compiled(domain) do
|
||||||
{:module, _} ->
|
{:module, _} ->
|
||||||
api
|
domain
|
||||||
|> Ash.Api.Info.resources()
|
|> Ash.Domain.Info.resources()
|
||||||
|> Enum.each(&Code.ensure_compiled/1)
|
|> Enum.each(&Code.ensure_compiled/1)
|
||||||
|
|
||||||
# TODO: We shouldn't need to make sure that the resources are compiled
|
# TODO: We shouldn't need to make sure that the resources are compiled
|
||||||
|
|
||||||
api
|
domain
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
Mix.raise("Could not load #{inspect(api)}, error: #{inspect(error)}. ")
|
Mix.raise("Could not load #{inspect(domain)}, error: #{inspect(error)}. ")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ defmodule Mix.Tasks.AshPostgres.Create do
|
||||||
|
|
||||||
@switches [
|
@switches [
|
||||||
quiet: :boolean,
|
quiet: :boolean,
|
||||||
apis: :string,
|
domains: :string,
|
||||||
no_compile: :boolean,
|
no_compile: :boolean,
|
||||||
no_deps_check: :boolean
|
no_deps_check: :boolean
|
||||||
]
|
]
|
||||||
|
@ -15,16 +15,16 @@ defmodule Mix.Tasks.AshPostgres.Create do
|
||||||
]
|
]
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Create the storage for repos in all resources for the given (or configured) apis.
|
Create the storage for repos in all resources for the given (or configured) domains.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
mix ash_postgres.create
|
mix ash_postgres.create
|
||||||
mix ash_postgres.create --apis MyApp.Api1,MyApp.Api2
|
mix ash_postgres.create --domains MyApp.Domain1,MyApp.Domain2
|
||||||
|
|
||||||
## Command line options
|
## Command line options
|
||||||
|
|
||||||
* `--apis` - the apis who's repos you want to migrate.
|
* `--domains` - the domains who's repos you want to migrate.
|
||||||
* `--quiet` - do not log output
|
* `--quiet` - do not log output
|
||||||
* `--no-compile` - do not compile before creating
|
* `--no-compile` - do not compile before creating
|
||||||
* `--no-deps-check` - do not compile before creating
|
* `--no-deps-check` - do not compile before creating
|
||||||
|
@ -41,7 +41,7 @@ defmodule Mix.Tasks.AshPostgres.Create do
|
||||||
["-r", to_string(repo)]
|
["-r", to_string(repo)]
|
||||||
end)
|
end)
|
||||||
|
|
||||||
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--apis")
|
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--domains")
|
||||||
|
|
||||||
Mix.Task.reenable("ecto.create")
|
Mix.Task.reenable("ecto.create")
|
||||||
Mix.Task.run("ecto.create", repo_args ++ rest_opts)
|
Mix.Task.run("ecto.create", repo_args ++ rest_opts)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
defmodule Mix.Tasks.AshPostgres.Drop do
|
defmodule Mix.Tasks.AshPostgres.Drop do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
|
|
||||||
@shortdoc "Drops the repository storage for the repos in the specified (or configured) apis"
|
@shortdoc "Drops the repository storage for the repos in the specified (or configured) domains"
|
||||||
@default_opts [force: false, force_drop: false]
|
@default_opts [force: false, force_drop: false]
|
||||||
|
|
||||||
@aliases [
|
@aliases [
|
||||||
|
@ -13,7 +13,7 @@ defmodule Mix.Tasks.AshPostgres.Drop do
|
||||||
force: :boolean,
|
force: :boolean,
|
||||||
force_drop: :boolean,
|
force_drop: :boolean,
|
||||||
quiet: :boolean,
|
quiet: :boolean,
|
||||||
apis: :string,
|
domains: :string,
|
||||||
no_compile: :boolean,
|
no_compile: :boolean,
|
||||||
no_deps_check: :boolean
|
no_deps_check: :boolean
|
||||||
]
|
]
|
||||||
|
@ -24,11 +24,11 @@ defmodule Mix.Tasks.AshPostgres.Drop do
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
mix ash_postgres.drop
|
mix ash_postgres.drop
|
||||||
mix ash_postgres.drop -r MyApp.Api1,MyApp.Api2
|
mix ash_postgres.drop -r MyApp.Repo1,MyApp.Repo2
|
||||||
|
|
||||||
## Command line options
|
## Command line options
|
||||||
|
|
||||||
* `--apis` - the apis who's repos should be dropped
|
* `--domains` - the domains who's repos should be dropped
|
||||||
* `-q`, `--quiet` - run the command quietly
|
* `-q`, `--quiet` - run the command quietly
|
||||||
* `-f`, `--force` - do not ask for confirmation when dropping the database.
|
* `-f`, `--force` - do not ask for confirmation when dropping the database.
|
||||||
Configuration is asked only when `:start_permanent` is set to true
|
Configuration is asked only when `:start_permanent` is set to true
|
||||||
|
@ -51,7 +51,7 @@ defmodule Mix.Tasks.AshPostgres.Drop do
|
||||||
["-r", to_string(repo)]
|
["-r", to_string(repo)]
|
||||||
end)
|
end)
|
||||||
|
|
||||||
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--apis")
|
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--domains")
|
||||||
|
|
||||||
Mix.Task.reenable("ecto.drop")
|
Mix.Task.reenable("ecto.drop")
|
||||||
Mix.Task.run("ecto.drop", repo_args ++ rest_opts)
|
Mix.Task.run("ecto.drop", repo_args ++ rest_opts)
|
||||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
* `apis` - a comma separated list of API modules, for which migrations will be generated
|
* `domains` - a comma separated list of Domain modules, for which migrations will be generated
|
||||||
* `snapshot-path` - a custom path to store the snapshots, defaults to "priv/resource_snapshots"
|
* `snapshot-path` - a custom path to store the snapshots, defaults to "priv/resource_snapshots"
|
||||||
* `migration-path` - a custom path to store the migrations, defaults to "priv".
|
* `migration-path` - a custom path to store the migrations, defaults to "priv".
|
||||||
Migrations are stored in a folder for each repo, so `priv/repo_name/migrations`
|
Migrations are stored in a folder for each repo, so `priv/repo_name/migrations`
|
||||||
|
@ -87,7 +87,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
||||||
{opts, _} =
|
{opts, _} =
|
||||||
OptionParser.parse!(args,
|
OptionParser.parse!(args,
|
||||||
strict: [
|
strict: [
|
||||||
apis: :string,
|
domains: :string,
|
||||||
snapshot_path: :string,
|
snapshot_path: :string,
|
||||||
migration_path: :string,
|
migration_path: :string,
|
||||||
tenant_migration_path: :string,
|
tenant_migration_path: :string,
|
||||||
|
@ -100,7 +100,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
apis = AshPostgres.MixHelpers.apis!(opts, args)
|
domains = AshPostgres.MixHelpers.domains!(opts, args)
|
||||||
|
|
||||||
opts =
|
opts =
|
||||||
opts
|
opts
|
||||||
|
@ -119,6 +119,6 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
||||||
""")
|
""")
|
||||||
end
|
end
|
||||||
|
|
||||||
AshPostgres.MigrationGenerator.generate(apis, opts)
|
AshPostgres.MigrationGenerator.generate(domains, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
||||||
import AshPostgres.MixHelpers,
|
import AshPostgres.MixHelpers,
|
||||||
only: [migrations_path: 2, tenant_migrations_path: 2, tenants: 2]
|
only: [migrations_path: 2, tenant_migrations_path: 2, tenants: 2]
|
||||||
|
|
||||||
@shortdoc "Runs the repository migrations for all repositories in the provided (or congigured) apis"
|
@shortdoc "Runs the repository migrations for all repositories in the provided (or congigured) domains"
|
||||||
|
|
||||||
@aliases [
|
@aliases [
|
||||||
n: :step
|
n: :step
|
||||||
|
@ -20,7 +20,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
||||||
pool_size: :integer,
|
pool_size: :integer,
|
||||||
log_sql: :boolean,
|
log_sql: :boolean,
|
||||||
strict_version_order: :boolean,
|
strict_version_order: :boolean,
|
||||||
apis: :string,
|
domains: :string,
|
||||||
no_compile: :boolean,
|
no_compile: :boolean,
|
||||||
no_deps_check: :boolean,
|
no_deps_check: :boolean,
|
||||||
migrations_path: :keep,
|
migrations_path: :keep,
|
||||||
|
@ -42,7 +42,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
||||||
specific version number, supply `--to version_number`. To migrate a
|
specific version number, supply `--to version_number`. To migrate a
|
||||||
specific number of times, use `--step n`.
|
specific number of times, use `--step n`.
|
||||||
|
|
||||||
This is only really useful if your api or apis only use a single repo.
|
This is only really useful if your domains only use a single repo.
|
||||||
If you have multiple repos and you want to run a single migration and/or
|
If you have multiple repos and you want to run a single migration and/or
|
||||||
migrate/roll them back to different points, you will need to use the
|
migrate/roll them back to different points, you will need to use the
|
||||||
ecto specific task, `mix ecto.migrate` and provide your repo name.
|
ecto specific task, `mix ecto.migrate` and provide your repo name.
|
||||||
|
@ -53,7 +53,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
mix ash_postgres.migrate
|
mix ash_postgres.migrate
|
||||||
mix ash_postgres.migrate --apis MyApp.Api1,MyApp.Api2
|
mix ash_postgres.migrate --domains MyApp.Domain1,MyApp.Domain2
|
||||||
|
|
||||||
mix ash_postgres.migrate -n 3
|
mix ash_postgres.migrate -n 3
|
||||||
mix ash_postgres.migrate --step 3
|
mix ash_postgres.migrate --step 3
|
||||||
|
@ -62,7 +62,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
||||||
|
|
||||||
## Command line options
|
## Command line options
|
||||||
|
|
||||||
* `--apis` - the apis who's repos should be migrated
|
* `--domains` - the domains who's repos should be migrated
|
||||||
|
|
||||||
* `--tenants` - Run the tenant migrations
|
* `--tenants` - Run the tenant migrations
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
||||||
|
|
||||||
rest_opts =
|
rest_opts =
|
||||||
args
|
args
|
||||||
|> AshPostgres.MixHelpers.delete_arg("--apis")
|
|> AshPostgres.MixHelpers.delete_arg("--domains")
|
||||||
|> AshPostgres.MixHelpers.delete_arg("--migrations-path")
|
|> AshPostgres.MixHelpers.delete_arg("--migrations-path")
|
||||||
|> AshPostgres.MixHelpers.delete_flag("--tenants")
|
|> AshPostgres.MixHelpers.delete_flag("--tenants")
|
||||||
|> AshPostgres.MixHelpers.delete_flag("--only-tenants")
|
|> AshPostgres.MixHelpers.delete_flag("--only-tenants")
|
||||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.AshPostgres.Rollback do
|
||||||
import AshPostgres.MixHelpers,
|
import AshPostgres.MixHelpers,
|
||||||
only: [migrations_path: 2, tenant_migrations_path: 2, tenants: 2]
|
only: [migrations_path: 2, tenant_migrations_path: 2, tenants: 2]
|
||||||
|
|
||||||
@shortdoc "Rolls back the repository migrations for all repositories in the provided (or configured) apis"
|
@shortdoc "Rolls back the repository migrations for all repositories in the provided (or configured) domains"
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Reverts applied migrations in the given repository.
|
Reverts applied migrations in the given repository.
|
||||||
|
@ -16,7 +16,7 @@ defmodule Mix.Tasks.AshPostgres.Rollback do
|
||||||
specific number of times, use `--step n`. To undo all applied
|
specific number of times, use `--step n`. To undo all applied
|
||||||
migrations, provide `--all`.
|
migrations, provide `--all`.
|
||||||
|
|
||||||
This is only really useful if your api or apis only use a single repo.
|
This is only really useful if your domains only use a single repo.
|
||||||
If you have multiple repos and you want to run a single migration and/or
|
If you have multiple repos and you want to run a single migration and/or
|
||||||
migrate/roll them back to different points, you will need to use the
|
migrate/roll them back to different points, you will need to use the
|
||||||
ecto specific task, `mix ecto.migrate` and provide your repo name.
|
ecto specific task, `mix ecto.migrate` and provide your repo name.
|
||||||
|
@ -30,7 +30,7 @@ defmodule Mix.Tasks.AshPostgres.Rollback do
|
||||||
mix ash_postgres.rollback --to 20080906120000
|
mix ash_postgres.rollback --to 20080906120000
|
||||||
|
|
||||||
## Command line options
|
## Command line options
|
||||||
* `--apis` - the apis who's repos should be rolledback
|
* `--domains` - the domains who's repos should be rolledback
|
||||||
* `--all` - revert all applied migrations
|
* `--all` - revert all applied migrations
|
||||||
* `--step` / `-n` - revert n number of applied migrations
|
* `--step` / `-n` - revert n number of applied migrations
|
||||||
* `--to` / `-v` - revert all migrations down to and including version
|
* `--to` / `-v` - revert all migrations down to and including version
|
||||||
|
@ -66,7 +66,7 @@ defmodule Mix.Tasks.AshPostgres.Rollback do
|
||||||
|
|
||||||
rest_opts =
|
rest_opts =
|
||||||
args
|
args
|
||||||
|> AshPostgres.MixHelpers.delete_arg("--apis")
|
|> AshPostgres.MixHelpers.delete_arg("--domains")
|
||||||
|> AshPostgres.MixHelpers.delete_arg("--migrations-path")
|
|> AshPostgres.MixHelpers.delete_arg("--migrations-path")
|
||||||
|> AshPostgres.MixHelpers.delete_flag("--tenants")
|
|> AshPostgres.MixHelpers.delete_flag("--tenants")
|
||||||
|> AshPostgres.MixHelpers.delete_flag("--only-tenants")
|
|> AshPostgres.MixHelpers.delete_flag("--only-tenants")
|
||||||
|
|
|
@ -44,6 +44,9 @@ defmodule AshPostgres.Repo do
|
||||||
@doc "Use this to inform the data layer about what extensions are installed"
|
@doc "Use this to inform the data layer about what extensions are installed"
|
||||||
@callback installed_extensions() :: [String.t() | module()]
|
@callback installed_extensions() :: [String.t() | module()]
|
||||||
|
|
||||||
|
@doc "Configure the version of postgres that is being used."
|
||||||
|
@callback pg_version() :: Version.t()
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Use this to inform the data layer about the oldest potential postgres version it will be run on.
|
Use this to inform the data layer about the oldest potential postgres version it will be run on.
|
||||||
|
|
||||||
|
@ -54,7 +57,6 @@ defmodule AshPostgres.Repo do
|
||||||
For things like `Fly.Repo`, where you might need to have more fine grained control over the repo module,
|
For things like `Fly.Repo`, where you might need to have more fine grained control over the repo module,
|
||||||
you can use the `define_ecto_repo?: false` option to `use AshPostgres.Repo`.
|
you can use the `define_ecto_repo?: false` option to `use AshPostgres.Repo`.
|
||||||
"""
|
"""
|
||||||
@callback min_pg_version() :: integer()
|
|
||||||
|
|
||||||
@callback on_transaction_begin(reason :: Ash.DataLayer.transaction_reason()) :: term
|
@callback on_transaction_begin(reason :: Ash.DataLayer.transaction_reason()) :: term
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ defmodule AshPostgres.Repo do
|
||||||
@callback migrations_path() :: String.t() | nil
|
@callback migrations_path() :: String.t() | nil
|
||||||
@doc "The default prefix(postgres schema) to use when building queries"
|
@doc "The default prefix(postgres schema) to use when building queries"
|
||||||
@callback default_prefix() :: String.t()
|
@callback default_prefix() :: String.t()
|
||||||
|
|
||||||
@doc "Allows overriding a given migration type for *all* fields, for example if you wanted to always use :timestamptz for :utc_datetime fields"
|
@doc "Allows overriding a given migration type for *all* fields, for example if you wanted to always use :timestamptz for :utc_datetime fields"
|
||||||
@callback override_migration_type(atom) :: atom
|
@callback override_migration_type(atom) :: atom
|
||||||
|
|
||||||
|
@ -88,7 +91,6 @@ defmodule AshPostgres.Repo do
|
||||||
def migrations_path, do: nil
|
def migrations_path, do: nil
|
||||||
def default_prefix, do: "public"
|
def default_prefix, do: "public"
|
||||||
def override_migration_type(type), do: type
|
def override_migration_type(type), do: type
|
||||||
def min_pg_version, do: 10
|
|
||||||
|
|
||||||
def transaction!(fun) do
|
def transaction!(fun) do
|
||||||
case fun.() do
|
case fun.() do
|
||||||
|
@ -224,8 +226,7 @@ defmodule AshPostgres.Repo do
|
||||||
all_tenants: 0,
|
all_tenants: 0,
|
||||||
tenant_migrations_path: 0,
|
tenant_migrations_path: 0,
|
||||||
default_prefix: 0,
|
default_prefix: 0,
|
||||||
override_migration_type: 1,
|
override_migration_type: 1
|
||||||
min_pg_version: 0
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
defmodule AshPostgres.Transformers.EnsureTableOrPolymorphic do
|
defmodule AshPostgres.Verifiers.EnsureTableOrPolymorphic do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Spark.Dsl.Transformer
|
use Spark.Dsl.Verifier
|
||||||
alias Spark.Dsl.Transformer
|
alias Spark.Dsl.Verifier
|
||||||
|
|
||||||
def transform(dsl) do
|
def verify(dsl) do
|
||||||
if Transformer.get_option(dsl, [:postgres], :polymorphic?) ||
|
if Verifier.get_option(dsl, [:postgres], :polymorphic?) ||
|
||||||
Transformer.get_option(dsl, [:postgres], :table) do
|
Verifier.get_option(dsl, [:postgres], :table) do
|
||||||
{:ok, dsl}
|
:ok
|
||||||
else
|
else
|
||||||
resource = Transformer.get_persisted(dsl, :module)
|
resource = Verifier.get_persisted(dsl, :module)
|
||||||
|
|
||||||
raise Spark.Error.DslError,
|
raise Spark.Error.DslError,
|
||||||
module: resource,
|
module: resource,
|
|
@ -1,10 +1,10 @@
|
||||||
defmodule AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchType do
|
defmodule AshPostgres.Verifiers.PreventAttributeMultitenancyAndNonFullMatchType do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Spark.Dsl.Transformer
|
use Spark.Dsl.Verifier
|
||||||
alias Spark.Dsl.Transformer
|
alias Spark.Dsl.Verifier
|
||||||
|
|
||||||
def transform(dsl) do
|
def verify(dsl) do
|
||||||
if Transformer.get_option(dsl, [:multitenancy], :strategy) == :attribute do
|
if Verifier.get_option(dsl, [:multitenancy], :strategy) == :attribute do
|
||||||
dsl
|
dsl
|
||||||
|> AshPostgres.DataLayer.Info.references()
|
|> AshPostgres.DataLayer.Info.references()
|
||||||
|> Enum.filter(&(&1.match_type && &1.match_type != :full))
|
|> Enum.filter(&(&1.match_type && &1.match_type != :full))
|
||||||
|
@ -14,7 +14,7 @@ defmodule AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchTy
|
||||||
if uses_attribute_strategy?(relationship) and
|
if uses_attribute_strategy?(relationship) and
|
||||||
not targets_primary_key?(relationship) and
|
not targets_primary_key?(relationship) and
|
||||||
not targets_multitenancy_attribute?(relationship) do
|
not targets_multitenancy_attribute?(relationship) do
|
||||||
resource = Transformer.get_persisted(dsl, :module)
|
resource = Verifier.get_persisted(dsl, :module)
|
||||||
|
|
||||||
raise Spark.Error.DslError,
|
raise Spark.Error.DslError,
|
||||||
module: resource,
|
module: resource,
|
||||||
|
@ -28,9 +28,9 @@ defmodule AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchTy
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
else
|
|
||||||
{:ok, dsl}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp uses_attribute_strategy?(relationship) do
|
defp uses_attribute_strategy?(relationship) do
|
|
@ -1,12 +1,10 @@
|
||||||
defmodule AshPostgres.Transformers.PreventMultidimensionalArrayAggregates do
|
defmodule AshPostgres.Verifiers.PreventMultidimensionalArrayAggregates do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Spark.Dsl.Transformer
|
use Spark.Dsl.Verifier
|
||||||
alias Spark.Dsl.Transformer
|
alias Spark.Dsl.Verifier
|
||||||
|
|
||||||
def after_compile?, do: true
|
def verify(dsl) do
|
||||||
|
resource = Verifier.get_persisted(dsl, :module)
|
||||||
def transform(dsl) do
|
|
||||||
resource = Transformer.get_persisted(dsl, :module)
|
|
||||||
|
|
||||||
dsl
|
dsl
|
||||||
|> Ash.Resource.Info.aggregates()
|
|> Ash.Resource.Info.aggregates()
|
||||||
|
@ -35,6 +33,6 @@ defmodule AshPostgres.Transformers.PreventMultidimensionalArrayAggregates do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, dsl}
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,23 +1,21 @@
|
||||||
defmodule AshPostgres.Transformers.ValidateReferences do
|
defmodule AshPostgres.Verifiers.ValidateReferences do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Spark.Dsl.Transformer
|
use Spark.Dsl.Verifier
|
||||||
alias Spark.Dsl.Transformer
|
alias Spark.Dsl.Verifier
|
||||||
|
|
||||||
def after_compile?, do: true
|
def verify(dsl) do
|
||||||
|
|
||||||
def transform(dsl) do
|
|
||||||
dsl
|
dsl
|
||||||
|> AshPostgres.DataLayer.Info.references()
|
|> AshPostgres.DataLayer.Info.references()
|
||||||
|> Enum.each(fn reference ->
|
|> Enum.each(fn reference ->
|
||||||
unless Ash.Resource.Info.relationship(dsl, reference.relationship) do
|
unless Ash.Resource.Info.relationship(dsl, reference.relationship) do
|
||||||
raise Spark.Error.DslError,
|
raise Spark.Error.DslError,
|
||||||
path: [:postgres, :references, reference.relationship],
|
path: [:postgres, :references, reference.relationship],
|
||||||
module: Transformer.get_persisted(dsl, :module),
|
module: Verifier.get_persisted(dsl, :module),
|
||||||
message:
|
message:
|
||||||
"Found reference configuration for relationship `#{reference.relationship}`, but no such relationship exists"
|
"Found reference configuration for relationship `#{reference.relationship}`, but no such relationship exists"
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, dsl}
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
37
lib/verifiers/verify_postgres_version.ex
Normal file
37
lib/verifiers/verify_postgres_version.ex
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
defmodule AshPostgres.Verifiers.VerifyPostgresVersion do
|
||||||
|
@moduledoc false
|
||||||
|
use Spark.Dsl.Verifier
|
||||||
|
|
||||||
|
def verify(dsl) do
|
||||||
|
read_repo = AshPostgres.DataLayer.Info.repo(dsl, :read)
|
||||||
|
mutate_repo = AshPostgres.DataLayer.Info.repo(dsl, :mutate)
|
||||||
|
|
||||||
|
read_version =
|
||||||
|
read_repo.pg_version() |> parse!(read_repo)
|
||||||
|
|
||||||
|
mutation_version = mutate_repo.pg_version() |> parse!(mutate_repo)
|
||||||
|
|
||||||
|
if Version.match?(read_version, ">= 14.0.0") && Version.match?(mutation_version, ">= 14.0.0") do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, "AshPostgres now only supports versions >= 14.0."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse!(%Version{} = version, _repo) do
|
||||||
|
version
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse!(version, repo) do
|
||||||
|
Version.parse!(version)
|
||||||
|
rescue
|
||||||
|
e ->
|
||||||
|
reraise ArgumentError,
|
||||||
|
"""
|
||||||
|
Failed to parse version in `#{inspect(repo)}.pg_version()`: #{inspect(version)}
|
||||||
|
|
||||||
|
Error: #{Exception.message(e)}
|
||||||
|
""",
|
||||||
|
__STACKTRACE__
|
||||||
|
end
|
||||||
|
end
|
8
mix.exs
8
mix.exs
|
@ -45,7 +45,7 @@ defmodule AshPostgres.MixProject do
|
||||||
if Mix.env() == :test do
|
if Mix.env() == :test do
|
||||||
def application() do
|
def application() do
|
||||||
[
|
[
|
||||||
applications: [:ecto, :ecto_sql, :jason, :ash, :postgrex, :tools, :benchee],
|
applications: [:ecto, :ecto_sql, :jason, :ash, :postgrex, :tools, :benchee, :xmerl],
|
||||||
mod: {AshPostgres.TestApp, []}
|
mod: {AshPostgres.TestApp, []}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -140,7 +140,6 @@ defmodule AshPostgres.MixProject do
|
||||||
EctoMigrationDefault
|
EctoMigrationDefault
|
||||||
],
|
],
|
||||||
Expressions: [
|
Expressions: [
|
||||||
AshPostgres.Functions.Fragment,
|
|
||||||
AshPostgres.Functions.TrigramSimilarity,
|
AshPostgres.Functions.TrigramSimilarity,
|
||||||
AshPostgres.Functions.ILike,
|
AshPostgres.Functions.ILike,
|
||||||
AshPostgres.Functions.Like,
|
AshPostgres.Functions.Like,
|
||||||
|
@ -157,7 +156,10 @@ defmodule AshPostgres.MixProject do
|
||||||
{:ecto, "~> 3.9"},
|
{:ecto, "~> 3.9"},
|
||||||
{:jason, "~> 1.0"},
|
{:jason, "~> 1.0"},
|
||||||
{:postgrex, ">= 0.0.0"},
|
{:postgrex, ">= 0.0.0"},
|
||||||
{:ash, ash_version("~> 2.19 and >= 2.20.3")},
|
{:spark, path: "../spark", override: true},
|
||||||
|
# dev/test dependencies
|
||||||
|
{:simple_sat, "~> 0.1"},
|
||||||
|
{:ash, ash_version(github: "ash-project/ash", branch: "3.0")},
|
||||||
{:benchee, "~> 1.1", only: [:dev, :test]},
|
{:benchee, "~> 1.1", only: [:dev, :test]},
|
||||||
{:git_ops, "~> 2.5", only: [:dev, :test]},
|
{:git_ops, "~> 2.5", only: [:dev, :test]},
|
||||||
{:ex_doc, github: "elixir-lang/ex_doc", only: [:dev, :test], runtime: false},
|
{:ex_doc, github: "elixir-lang/ex_doc", only: [:dev, :test], runtime: false},
|
||||||
|
|
12
mix.lock
12
mix.lock
|
@ -1,5 +1,5 @@
|
||||||
%{
|
%{
|
||||||
"ash": {:hex, :ash, "2.20.3", "2ded1295fd20e2a45b01c678fe93c51397384ec5e5e4babc80f1ae9ce896ca82", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.6", [hex: :reactor, repo: "hexpm", optional: false]}, {:spark, ">= 1.1.55 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b53374c6c70da21bb8d53fefb88e1b0dc7a6fd8cf48ecaff4d6e57d2e69afbca"},
|
"ash": {:git, "https://github.com/ash-project/ash.git", "37587cbc580c30248044eea01d461f578df6cbc4", [branch: "3.0"]},
|
||||||
"benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"},
|
"benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"},
|
||||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||||
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
||||||
|
@ -8,11 +8,9 @@
|
||||||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
||||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
||||||
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
||||||
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
|
|
||||||
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
|
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
|
||||||
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
|
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"},
|
"ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.8.2", "cd4a5a75891362e9207adaac7e66223fd256ec2518ae013af7f10c9c85b50b5c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "9d9607d640c372a7291e5a56ce655aa2351897929be20bd211648fdb79e725dc"},
|
|
||||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
|
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
|
||||||
"ex_check": {:hex, :ex_check, "0.15.0", "074b94c02de11c37bba1ca82ae5cc4926e6ccee862e57a485b6ba60fca2d8dc1", [:mix], [], "hexpm", "33848031a0c7e4209c3b4369ce154019788b5219956220c35ca5474299fb6a0e"},
|
"ex_check": {:hex, :ex_check, "0.15.0", "074b94c02de11c37bba1ca82ae5cc4926e6ccee862e57a485b6ba60fca2d8dc1", [:mix], [], "hexpm", "33848031a0c7e4209c3b4369ce154019788b5219956220c35ca5474299fb6a0e"},
|
||||||
|
@ -26,14 +24,14 @@
|
||||||
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
|
||||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
|
"makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
|
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
||||||
"picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"},
|
|
||||||
"postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"},
|
"postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"},
|
||||||
"reactor": {:hex, :reactor, "0.7.0", "fb76d23d95829b28ac9b9d654620c43c890c6a32ea26ac13086c48540b34e8c5", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 1.0", [hex: :spark, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4310da820d753aafd7dc4ee8cc687b84565dd6d9536e38806ee211da792178fd"},
|
"reactor": {:hex, :reactor, "0.8.1", "1aec71d16083901277727c8162f6dd0f07e80f5ca98911b6ef4f2c95e6e62758", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ae3936d97a3e4a316744f70c77b85345b08b70da334024c26e6b5eb8ede1246b"},
|
||||||
|
"simple_sat": {:hex, :simple_sat, "0.1.1", "68a5ebe6f6d5956bd806e4881c495692c14580a2f1a4420488985abd0fba2119", [:mix], [], "hexpm", "63571218f92ff029838df7645eb8f0c38df8ed60d2d14578412a8d142a94471e"},
|
||||||
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
||||||
"sourceror": {:hex, :sourceror, "1.0.2", "c5e86fdc14881f797749d1fe5df017ca66727a8146e7ee3e736605a3df78f3e6", [:mix], [], "hexpm", "832335e87d0913658f129d58b2a7dc0490ddd4487b02de6d85bca0169ec2bd79"},
|
"sourceror": {:hex, :sourceror, "1.0.2", "c5e86fdc14881f797749d1fe5df017ca66727a8146e7ee3e736605a3df78f3e6", [:mix], [], "hexpm", "832335e87d0913658f129d58b2a7dc0490ddd4487b02de6d85bca0169ec2bd79"},
|
||||||
"spark": {:hex, :spark, "1.1.55", "d20c3f899b23d841add29edc912ffab4463d3bb801bc73448738631389291d2e", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "bbc15a4223d8e610c81ceca825d5d0bae3738d1c4ac4dbb1061749966776c3f1"},
|
"spark": {:hex, :spark, "2.1.6", "15c6725836607322e867b40fb6bdeb13f4606d48a001d2a74518559678e26895", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "6838d226e83acedb3a6169169bfc2d7afde19ef6a37fb14f0572d39db9d56c7b"},
|
||||||
|
"splode": {:hex, :splode, "0.2.0", "a1f3b5a8e7c957be495bf0f22dd9e0567a87ec63559963a0ce0c3f0e8dfacedc", [:mix], [], "hexpm", "7cfecc5913ff7feeb04f143e2494cfa7bc6d5bb5bec70f7ffac94c18ea97f303"},
|
||||||
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
|
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
|
||||||
"stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"},
|
"stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"},
|
||||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,8 @@ defmodule AshPostgresTest do
|
||||||
|
|
||||||
test "transaction metadata is given to on_transaction_begin" do
|
test "transaction metadata is given to on_transaction_begin" do
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_receive %{
|
assert_receive %{
|
||||||
type: :create,
|
type: :create,
|
||||||
|
@ -15,8 +15,8 @@ defmodule AshPostgresTest do
|
||||||
test "filter policies are applied" do
|
test "filter policies are applied" do
|
||||||
post =
|
post =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Changeset.new(%{title: "good"})
|
|> Ash.Changeset.for_create(:create, %{title: "good"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Forbidden, fn ->
|
assert_raise Ash.Error.Forbidden, fn ->
|
||||||
post
|
post
|
||||||
|
@ -24,13 +24,13 @@ defmodule AshPostgresTest do
|
||||||
authorize?: true,
|
authorize?: true,
|
||||||
actor: %{id: Ash.UUID.generate()}
|
actor: %{id: Ash.UUID.generate()}
|
||||||
)
|
)
|
||||||
|> AshPostgres.Test.Api.update!()
|
|> Ash.update!()
|
||||||
|> Map.get(:title)
|
|> Map.get(:title)
|
||||||
end
|
end
|
||||||
|
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:update, %{title: "okay"}, authorize?: true)
|
|> Ash.Changeset.for_update(:update, %{title: "okay"}, authorize?: true)
|
||||||
|> AshPostgres.Test.Api.update!()
|
|> Ash.update!()
|
||||||
|> Map.get(:title)
|
|> Map.get(:title)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.AtomicsTest do
|
defmodule AshPostgres.AtomicsTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
import Ash.Expr
|
import Ash.Expr
|
||||||
|
|
||||||
|
@ -10,55 +10,57 @@ defmodule AshPostgres.AtomicsTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{id: id, title: "foo", price: 1}, upsert?: true)
|
|> Ash.Changeset.for_create(:create, %{id: id, title: "foo", price: 1}, upsert?: true)
|
||||||
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{id: id, title: "foo", price: 1}, upsert?: true)
|
|> Ash.Changeset.for_create(:create, %{id: id, title: "foo", price: 1}, upsert?: true)
|
||||||
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{price: 2}] = Post |> Api.read!()
|
assert [%{price: 2}] = Post |> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "a basic atomic works" do
|
test "a basic atomic works" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{price: 2} =
|
assert %{price: 2} =
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update, %{})
|
||||||
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "an atomic works with a datetime" do
|
test "an atomic works with a datetime" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
now = DateTime.utc_now()
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
assert %{created_at: ^now} =
|
assert %{created_at: ^now} =
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.atomic_update(:created_at, expr(^now))
|
|> Ash.Changeset.atomic_update(:created_at, expr(^now))
|
||||||
|> Api.update!()
|
|> Ash.Changeset.for_update(:update, %{})
|
||||||
|
|> Ash.update!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "an atomic that violates a constraint will return the proper error" do
|
test "an atomic that violates a constraint will return the proper error" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Invalid, ~r/does not exist/, fn ->
|
assert_raise Ash.Error.Invalid, ~r/does not exist/, fn ->
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.atomic_update(:organization_id, Ash.UUID.generate())
|
|> Ash.Changeset.atomic_update(:organization_id, Ash.UUID.generate())
|
||||||
|> Api.update!()
|
|> Ash.Changeset.for_update(:update, %{})
|
||||||
|
|> Ash.update!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -66,13 +68,13 @@ defmodule AshPostgres.AtomicsTest do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post =
|
post =
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update, %{})
|
||||||
|> Ash.Changeset.atomic_update(:score, expr(score_after_winning))
|
|> Ash.Changeset.atomic_update(:score, expr(score_after_winning))
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
assert post.score == 1
|
assert post.score == 1
|
||||||
end
|
end
|
||||||
|
@ -81,7 +83,7 @@ defmodule AshPostgres.AtomicsTest do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert Post.increment_score!(post, 2).score == 2
|
assert Post.increment_score!(post, 2).score == 2
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
defmodule AshPostgres.BulkCreateTest do
|
defmodule AshPostgres.BulkCreateTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
describe "bulk creates" do
|
describe "bulk creates" do
|
||||||
test "bulk creates insert each input" do
|
test "bulk creates insert each input" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
assert [%{title: "fred"}, %{title: "george"}] =
|
assert [%{title: "fred"}, %{title: "george"}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(:title)
|
|> Ash.Query.sort(:title)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk creates can be streamed" do
|
test "bulk creates can be streamed" do
|
||||||
assert [{:ok, %{title: "fred"}}, {:ok, %{title: "george"}}] =
|
assert [{:ok, %{title: "fred"}}, {:ok, %{title: "george"}}] =
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create,
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create,
|
||||||
return_stream?: true,
|
return_stream?: true,
|
||||||
return_records?: true
|
return_records?: true
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
{:ok, %{title: "fred", uniq_one: "one", uniq_two: "two", price: 10}},
|
{:ok, %{title: "fred", uniq_one: "one", uniq_two: "two", price: 10}},
|
||||||
{:ok, %{title: "george", uniq_one: "three", uniq_two: "four", price: 20}}
|
{:ok, %{title: "george", uniq_one: "three", uniq_two: "four", price: 20}}
|
||||||
] =
|
] =
|
||||||
Api.bulk_create!(
|
Ash.bulk_create!(
|
||||||
[
|
[
|
||||||
%{title: "fred", uniq_one: "one", uniq_two: "two", price: 10},
|
%{title: "fred", uniq_one: "one", uniq_two: "two", price: 10},
|
||||||
%{title: "george", uniq_one: "three", uniq_two: "four", price: 20}
|
%{title: "george", uniq_one: "three", uniq_two: "four", price: 20}
|
||||||
|
@ -42,7 +42,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
{:ok, %{title: "fred", uniq_one: "one", uniq_two: "two", price: 1000}},
|
{:ok, %{title: "fred", uniq_one: "one", uniq_two: "two", price: 1000}},
|
||||||
{:ok, %{title: "george", uniq_one: "three", uniq_two: "four", price: 20_000}}
|
{:ok, %{title: "george", uniq_one: "three", uniq_two: "four", price: 20_000}}
|
||||||
] =
|
] =
|
||||||
Api.bulk_create!(
|
Ash.bulk_create!(
|
||||||
[
|
[
|
||||||
%{title: "something", uniq_one: "one", uniq_two: "two", price: 1000},
|
%{title: "something", uniq_one: "one", uniq_two: "two", price: 1000},
|
||||||
%{title: "else", uniq_one: "three", uniq_two: "four", price: 20_000}
|
%{title: "else", uniq_one: "three", uniq_two: "four", price: 20_000}
|
||||||
|
@ -76,7 +76,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
# id: org_id,
|
# id: org_id,
|
||||||
# title: "Avengers"
|
# title: "Avengers"
|
||||||
# })
|
# })
|
||||||
# |> Api.create!()
|
# |> Ash.create!()
|
||||||
|
|
||||||
# assert [
|
# assert [
|
||||||
# {:ok,
|
# {:ok,
|
||||||
|
@ -94,7 +94,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
# organization_id: org_id
|
# organization_id: org_id
|
||||||
# }}
|
# }}
|
||||||
# ] =
|
# ] =
|
||||||
# Api.bulk_create!(
|
# Ash.bulk_create!(
|
||||||
# [
|
# [
|
||||||
# %{
|
# %{
|
||||||
# name: "Tony Stark",
|
# name: "Tony Stark",
|
||||||
|
@ -135,7 +135,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
# role: "master in chief"
|
# role: "master in chief"
|
||||||
# }}
|
# }}
|
||||||
# ] =
|
# ] =
|
||||||
# Api.bulk_create!(
|
# Ash.bulk_create!(
|
||||||
# [
|
# [
|
||||||
# %{
|
# %{
|
||||||
# name: "Tony Stark",
|
# name: "Tony Stark",
|
||||||
|
@ -169,7 +169,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
# end
|
# end
|
||||||
|
|
||||||
test "bulk creates can create relationships" do
|
test "bulk creates can create relationships" do
|
||||||
Api.bulk_create!(
|
Ash.bulk_create!(
|
||||||
[%{title: "fred", rating: %{score: 5}}, %{title: "george", rating: %{score: 0}}],
|
[%{title: "fred", rating: %{score: 5}}, %{title: "george", rating: %{score: 0}}],
|
||||||
Post,
|
Post,
|
||||||
:create
|
:create
|
||||||
|
@ -182,14 +182,14 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(:title)
|
|> Ash.Query.sort(:title)
|
||||||
|> Ash.Query.load(:ratings)
|
|> Ash.Query.load(:ratings)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "validation errors" do
|
describe "validation errors" do
|
||||||
test "skips invalid by default" do
|
test "skips invalid by default" do
|
||||||
assert %{records: [_], errors: [_]} =
|
assert %{records: [_], errors: [_]} =
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "not allowed"}], Post, :create,
|
Ash.bulk_create!([%{title: "fred"}, %{title: "not allowed"}], Post, :create,
|
||||||
return_records?: true,
|
return_records?: true,
|
||||||
return_errors?: true
|
return_errors?: true
|
||||||
)
|
)
|
||||||
|
@ -197,7 +197,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
|
|
||||||
test "returns errors in the stream" do
|
test "returns errors in the stream" do
|
||||||
assert [{:ok, _}, {:error, _}] =
|
assert [{:ok, _}, {:error, _}] =
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "not allowed"}], Post, :create,
|
Ash.bulk_create!([%{title: "fred"}, %{title: "not allowed"}], Post, :create,
|
||||||
return_records?: true,
|
return_records?: true,
|
||||||
return_stream?: true,
|
return_stream?: true,
|
||||||
return_errors?: true
|
return_errors?: true
|
||||||
|
@ -209,7 +209,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
describe "database errors" do
|
describe "database errors" do
|
||||||
test "database errors affect the entire batch" do
|
test "database errors affect the entire batch" do
|
||||||
# assert %{records: [_], errors: [_]} =
|
# assert %{records: [_], errors: [_]} =
|
||||||
Api.bulk_create(
|
Ash.bulk_create(
|
||||||
[%{title: "fred"}, %{title: "george", organization_id: Ash.UUID.generate()}],
|
[%{title: "fred"}, %{title: "george", organization_id: Ash.UUID.generate()}],
|
||||||
Post,
|
Post,
|
||||||
:create,
|
:create,
|
||||||
|
@ -219,11 +219,11 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
assert [] =
|
assert [] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(:title)
|
|> Ash.Query.sort(:title)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "database errors don't affect other batches" do
|
test "database errors don't affect other batches" do
|
||||||
Api.bulk_create(
|
Ash.bulk_create(
|
||||||
[%{title: "george", organization_id: Ash.UUID.generate()}, %{title: "fred"}],
|
[%{title: "george", organization_id: Ash.UUID.generate()}, %{title: "fred"}],
|
||||||
Post,
|
Post,
|
||||||
:create,
|
:create,
|
||||||
|
@ -234,7 +234,7 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
assert [%{title: "fred"}] =
|
assert [%{title: "fred"}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(:title)
|
|> Ash.Query.sort(:title)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,50 +1,50 @@
|
||||||
defmodule AshPostgres.BulkDestroyTest do
|
defmodule AshPostgres.BulkDestroyTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Expr
|
require Ash.Expr
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "bulk destroys can run with nothing in the table" do
|
test "bulk destroys can run with nothing in the table" do
|
||||||
Api.bulk_destroy!(Post, :update, %{title: "new_title"})
|
Ash.bulk_destroy!(Post, :update, %{title: "new_title"})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk destroys destroy everything pertaining to the query" do
|
test "bulk destroys destroy everything pertaining to the query" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Api.bulk_destroy!(Post, :update, %{})
|
Ash.bulk_destroy!(Post, :update, %{})
|
||||||
|
|
||||||
assert Api.read!(Post) == []
|
assert Ash.read!(Post) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk destroys only apply to things that the query produces" do
|
test "bulk destroys only apply to things that the query produces" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(title == "fred")
|
|> Ash.Query.filter(title == "fred")
|
||||||
|> Api.bulk_destroy!(:update, %{})
|
|> Ash.bulk_destroy!(:update, %{})
|
||||||
|
|
||||||
# 😢 sad
|
# 😢 sad
|
||||||
assert [%{title: "george"}] = Api.read!(Post)
|
assert [%{title: "george"}] = Ash.read!(Post)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the query can join to related tables when necessary" do
|
test "the query can join to related tables when necessary" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(author.first_name == "fred" or title == "fred")
|
|> Ash.Query.filter(author.first_name == "fred" or title == "fred")
|
||||||
|> Api.bulk_destroy!(:update, %{}, return_records?: true)
|
|> Ash.bulk_destroy!(:update, %{}, return_records?: true)
|
||||||
|
|
||||||
assert [%{title: "george"}] = Api.read!(Post)
|
assert [%{title: "george"}] = Ash.read!(Post)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk destroys can be done even on stream inputs" do
|
test "bulk destroys can be done even on stream inputs" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Api.bulk_destroy!(:destroy, %{})
|
|> Ash.bulk_destroy!(:destroy, %{}, strategy: :stream, return_errors?: true)
|
||||||
|
|
||||||
assert [] = Api.read!(Post)
|
assert [] = Ash.read!(Post)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
defmodule AshPostgres.BulkUpdateTest do
|
defmodule AshPostgres.BulkUpdateTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Expr
|
require Ash.Expr
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "bulk updates can run with nothing in the table" do
|
test "bulk updates can run with nothing in the table" do
|
||||||
Api.bulk_update!(Post, :update, %{title: "new_title"})
|
Ash.bulk_update!(Post, :update, %{title: "new_title"})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk updates update everything pertaining to the query" do
|
test "bulk updates update everything pertaining to the query" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Api.bulk_update!(Post, :update, %{},
|
Ash.bulk_update!(Post, :update, %{},
|
||||||
atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")}
|
atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")}
|
||||||
)
|
)
|
||||||
|
|
||||||
posts = Api.read!(Post)
|
posts = Ash.read!(Post)
|
||||||
assert Enum.all?(posts, &String.ends_with?(&1.title, "_stuff"))
|
assert Enum.all?(posts, &String.ends_with?(&1.title, "_stuff"))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "a map can be given as input" do
|
test "a map can be given as input" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Api.bulk_update!(
|
|> Ash.bulk_update!(
|
||||||
:update,
|
:update,
|
||||||
%{list_of_stuff: [%{a: 1}]},
|
%{list_of_stuff: [%{a: 1}]},
|
||||||
return_records?: true,
|
return_records?: true,
|
||||||
|
@ -36,25 +36,25 @@ defmodule AshPostgres.BulkUpdateTest do
|
||||||
|
|
||||||
test "a map can be given as input on a regular update" do
|
test "a map can be given as input on a regular update" do
|
||||||
%{records: [post | _]} =
|
%{records: [post | _]} =
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create,
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create,
|
||||||
return_records?: true
|
return_records?: true
|
||||||
)
|
)
|
||||||
|
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:update, %{list_of_stuff: [%{a: [:a, :b]}, %{a: [:c, :d]}]})
|
|> Ash.Changeset.for_update(:update, %{list_of_stuff: [%{a: [:a, :b]}, %{a: [:c, :d]}]})
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk updates only apply to things that the query produces" do
|
test "bulk updates only apply to things that the query produces" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(title == "fred")
|
|> Ash.Query.filter(title == "fred")
|
||||||
|> Api.bulk_update!(:update, %{}, atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")})
|
|> Ash.bulk_update!(:update, %{}, atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")})
|
||||||
|
|
||||||
titles =
|
titles =
|
||||||
Post
|
Post
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.map(& &1.title)
|
|> Enum.map(& &1.title)
|
||||||
|> Enum.sort()
|
|> Enum.sort()
|
||||||
|
|
||||||
|
@ -62,15 +62,15 @@ defmodule AshPostgres.BulkUpdateTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the query can join to related tables when necessary" do
|
test "the query can join to related tables when necessary" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(author.first_name == "fred" or title == "fred")
|
|> Ash.Query.filter(author.first_name == "fred" or title == "fred")
|
||||||
|> Api.bulk_update!(:update, %{}, atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")})
|
|> Ash.bulk_update!(:update, %{}, atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")})
|
||||||
|
|
||||||
titles =
|
titles =
|
||||||
Post
|
Post
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.map(& &1.title)
|
|> Enum.map(& &1.title)
|
||||||
|> Enum.sort()
|
|> Enum.sort()
|
||||||
|
|
||||||
|
@ -78,18 +78,19 @@ defmodule AshPostgres.BulkUpdateTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk updates can be done even on stream inputs" do
|
test "bulk updates can be done even on stream inputs" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Api.bulk_update!(:update, %{},
|
|> Ash.bulk_update!(:update, %{},
|
||||||
atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")},
|
atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")},
|
||||||
return_records?: true
|
return_records?: true,
|
||||||
|
strategy: [:stream]
|
||||||
)
|
)
|
||||||
|
|
||||||
titles =
|
titles =
|
||||||
Post
|
Post
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.map(& &1.title)
|
|> Enum.map(& &1.title)
|
||||||
|> Enum.sort()
|
|> Enum.sort()
|
||||||
|
|
||||||
|
@ -97,14 +98,17 @@ defmodule AshPostgres.BulkUpdateTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "bulk updates that require initial data must use streaming" do
|
test "bulk updates that require initial data must use streaming" do
|
||||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
|
||||||
Post
|
assert_raise Ash.Error.Invalid, ~r/had no matching bulk strategy that could be used/, fn ->
|
||||||
|> Ash.Query.for_read(:paginated, authorize?: true)
|
Post
|
||||||
|> Api.bulk_update!(:requires_initial_data, %{},
|
|> Ash.Query.for_read(:paginated, authorize?: true)
|
||||||
authorize?: true,
|
|> Ash.bulk_update!(:requires_initial_data, %{},
|
||||||
allow_stream_with: :full_read,
|
authorize?: true,
|
||||||
authorize_query?: false
|
allow_stream_with: :full_read,
|
||||||
)
|
authorize_query?: false,
|
||||||
|
return_errors?: true
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.CalculationTest do
|
defmodule AshPostgres.CalculationTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Account, Api, Author, Comment, Post, User}
|
alias AshPostgres.Test.{Account, Author, Comment, Post, User}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
import Ash.Expr
|
import Ash.Expr
|
||||||
|
@ -8,48 +8,48 @@ defmodule AshPostgres.CalculationTest do
|
||||||
test "an expression calculation can be filtered on" do
|
test "an expression calculation can be filtered on" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2 =
|
post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post3 =
|
post3 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title3"})
|
|> Ash.Changeset.for_create(:create, %{title: "title3"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "_"})
|
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "_"})
|
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "_"})
|
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
post2
|
post2
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
assert [%{c_times_p: 6, title: "match"}] =
|
assert [%{c_times_p: 6, title: "match"}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:c_times_p)
|
|> Ash.Query.load(:c_times_p)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.filter(&(&1.c_times_p == 6))
|
|> Enum.filter(&(&1.c_times_p == 6))
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
|
@ -57,12 +57,12 @@ defmodule AshPostgres.CalculationTest do
|
||||||
] =
|
] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(c_times_p == 6)
|
|> Ash.Query.filter(c_times_p == 6)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(author: [has_posts: true])
|
|> Ash.Query.filter(author: [has_posts: true])
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can refer to to_one path attributes in filters" do
|
test "calculations can refer to to_one path attributes in filters" do
|
||||||
|
@ -72,18 +72,18 @@ defmodule AshPostgres.CalculationTest do
|
||||||
first_name: "Foo",
|
first_name: "Foo",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{author_first_name_calc: "Foo"}] =
|
assert [%{author_first_name_calc: "Foo"}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(author_first_name_calc == "Foo")
|
|> Ash.Query.filter(author_first_name_calc == "Foo")
|
||||||
|> Ash.Query.load(:author_first_name_calc)
|
|> Ash.Query.load(:author_first_name_calc)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can refer to to_one path attributes in sorts" do
|
test "calculations can refer to to_one path attributes in sorts" do
|
||||||
|
@ -93,44 +93,44 @@ defmodule AshPostgres.CalculationTest do
|
||||||
first_name: "Foo",
|
first_name: "Foo",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{author_first_name_calc: "Foo"}] =
|
assert [%{author_first_name_calc: "Foo"}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(:author_first_name_calc)
|
|> Ash.Query.sort(:author_first_name_calc)
|
||||||
|> Ash.Query.load(:author_first_name_calc)
|
|> Ash.Query.load(:author_first_name_calc)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can refer to embedded attributes" do
|
test "calculations can refer to embedded attributes" do
|
||||||
author =
|
author =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{title: "Mr."} =
|
assert %{title: "Mr."} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load(:title)
|
|> Ash.Query.load(:title)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can use the || operator" do
|
test "calculations can use the || operator" do
|
||||||
author =
|
author =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{first_name_or_bob: "bob"} =
|
assert %{first_name_or_bob: "bob"} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load(:first_name_or_bob)
|
|> Ash.Query.load(:first_name_or_bob)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can use the && operator" do
|
test "calculations can use the && operator" do
|
||||||
|
@ -140,60 +140,60 @@ defmodule AshPostgres.CalculationTest do
|
||||||
first_name: "fred",
|
first_name: "fred",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{first_name_and_bob: "bob"} =
|
assert %{first_name_and_bob: "bob"} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load(:first_name_and_bob)
|
|> Ash.Query.load(:first_name_and_bob)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can be used in related filters" do
|
test "calculations can be used in related filters" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2 =
|
post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post3 =
|
post3 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title3"})
|
|> Ash.Changeset.for_create(:create, %{title: "title3"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no_match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no_match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post2, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post2, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
post2
|
post2
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
posts_query =
|
posts_query =
|
||||||
Post
|
Post
|
||||||
|
@ -202,7 +202,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
assert %{post: %{c_times_p: 6}} =
|
assert %{post: %{c_times_p: 6}} =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.load(post: posts_query)
|
|> Ash.Query.load(post: posts_query)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.filter(&(&1.post.c_times_p == 6))
|
|> Enum.filter(&(&1.post.c_times_p == 6))
|
||||||
|> Enum.at(0)
|
|> Enum.at(0)
|
||||||
|
|
||||||
|
@ -214,20 +214,20 @@ defmodule AshPostgres.CalculationTest do
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{post: %{c_times_p: 6, title: "match"}}
|
%{post: %{c_times_p: 6, title: "match"}}
|
||||||
] = Api.read!(query)
|
] = Ash.read!(query)
|
||||||
|
|
||||||
post |> Api.load!(:c_times_p)
|
post |> Ash.load!(:c_times_p)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "concat calculation can be filtered on" do
|
test "concat calculation can be filtered on" do
|
||||||
author =
|
author =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "not", last_name: "match"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "not", last_name: "match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
author_id = author.id
|
author_id = author.id
|
||||||
|
|
||||||
|
@ -235,60 +235,60 @@ defmodule AshPostgres.CalculationTest do
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.load(:full_name)
|
|> Ash.Query.load(:full_name)
|
||||||
|> Ash.Query.filter(full_name == "is match")
|
|> Ash.Query.filter(full_name == "is match")
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations that refer to aggregates in comparison expressions can be filtered on" do
|
test "calculations that refer to aggregates in comparison expressions can be filtered on" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:has_future_comment)
|
|> Ash.Query.load(:has_future_comment)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations that refer to aggregates can be authorized" do
|
test "calculations that refer to aggregates can be authorized" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "comment"})
|
|> Ash.Changeset.for_create(:create, %{title: "comment"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{has_future_comment: false} =
|
assert %{has_future_comment: false} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
||||||
|> Ash.Query.for_read(:allow_any, %{})
|
|> Ash.Query.for_read(:allow_any, %{})
|
||||||
|> Api.read_one!(authorize?: true)
|
|> Ash.read_one!(authorize?: true)
|
||||||
|
|
||||||
assert %{has_future_comment: true} =
|
assert %{has_future_comment: true} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
||||||
|> Ash.Query.for_read(:allow_any, %{})
|
|> Ash.Query.for_read(:allow_any, %{})
|
||||||
|> Api.read_one!(authorize?: false)
|
|> Ash.read_one!(authorize?: false)
|
||||||
|
|
||||||
assert %{has_future_comment: false} =
|
assert %{has_future_comment: false} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.for_read(:allow_any, %{})
|
|> Ash.Query.for_read(:allow_any, %{})
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|> Api.load!([:has_future_comment, :latest_comment_created_at], authorize?: true)
|
|> Ash.load!([:has_future_comment, :latest_comment_created_at], authorize?: true)
|
||||||
|
|
||||||
assert %{has_future_comment: true} =
|
assert %{has_future_comment: true} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.for_read(:allow_any, %{})
|
|> Ash.Query.for_read(:allow_any, %{})
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|> Api.load!([:has_future_comment, :latest_comment_created_at], authorize?: false)
|
|> Ash.load!([:has_future_comment, :latest_comment_created_at], authorize?: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "conditional calculations can be filtered on" do
|
test "conditional calculations can be filtered on" do
|
||||||
author =
|
author =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "tom"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "tom"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
author_id = author.id
|
author_id = author.id
|
||||||
|
|
||||||
|
@ -296,45 +296,45 @@ defmodule AshPostgres.CalculationTest do
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.load([:conditional_full_name, :full_name])
|
|> Ash.Query.load([:conditional_full_name, :full_name])
|
||||||
|> Ash.Query.filter(conditional_full_name == "(none)")
|
|> Ash.Query.filter(conditional_full_name == "(none)")
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "parameterized calculations can be filtered on" do
|
test "parameterized calculations can be filtered on" do
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{param_full_name: "tom holland"} =
|
assert %{param_full_name: "tom holland"} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.load(:param_full_name)
|
|> Ash.Query.load(:param_full_name)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|
|
||||||
assert %{param_full_name: "tom~holland"} =
|
assert %{param_full_name: "tom~holland"} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.load(param_full_name: [separator: "~"])
|
|> Ash.Query.load(param_full_name: [separator: "~"])
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|
|
||||||
assert %{} =
|
assert %{} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(param_full_name(separator: "~") == "tom~holland")
|
|> Ash.Query.filter(param_full_name(separator: "~") == "tom~holland")
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "parameterized related calculations can be filtered on" do
|
test "parameterized related calculations can be filtered on" do
|
||||||
author =
|
author =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{title: "match"} =
|
assert %{title: "match"} =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(author.param_full_name(separator: "~") == "tom~holland")
|
|> Ash.Query.filter(author.param_full_name(separator: "~") == "tom~holland")
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|
|
||||||
assert %{title: "match"} =
|
assert %{title: "match"} =
|
||||||
Comment
|
Comment
|
||||||
|
@ -342,93 +342,97 @@ defmodule AshPostgres.CalculationTest do
|
||||||
author.param_full_name(separator: "~") == "tom~holland" and
|
author.param_full_name(separator: "~") == "tom~holland" and
|
||||||
author.param_full_name(separator: " ") == "tom holland"
|
author.param_full_name(separator: " ") == "tom holland"
|
||||||
)
|
)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "parameterized calculations can be sorted on" do
|
test "parameterized calculations can be sorted on" do
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "abc", last_name: "def"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "abc", last_name: "def"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{first_name: "abc"}, %{first_name: "tom"}] =
|
assert [%{first_name: "abc"}, %{first_name: "tom"}] =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.sort(param_full_name: [separator: "~"])
|
|> Ash.Query.sort(param_full_name: [separator: "~"])
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations using if and literal boolean results can run" do
|
test "calculations using if and literal boolean results can run" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:was_created_in_the_last_month)
|
|> Ash.Query.load(:was_created_in_the_last_month)
|
||||||
|> Ash.Query.filter(was_created_in_the_last_month == true)
|
|> Ash.Query.filter(was_created_in_the_last_month == true)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "nested conditional calculations can be loaded" do
|
test "nested conditional calculations can be loaded" do
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{last_name: "holland"})
|
|> Ash.Changeset.for_create(:create, %{last_name: "holland"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{first_name: "tom"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "tom"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{nested_conditional: "No First Name"}, %{nested_conditional: "No Last Name"}] =
|
assert [%{nested_conditional: "No First Name"}, %{nested_conditional: "No Last Name"}] =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.load(:nested_conditional)
|
|> Ash.Query.load(:nested_conditional)
|
||||||
|> Ash.Query.sort(:nested_conditional)
|
|> Ash.Query.sort(:nested_conditional)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations load nullable timestamp aggregates compared to a fragment" do
|
test "calculations load nullable timestamp aggregates compared to a fragment" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "aaa", likes: 1, arbitrary_timestamp: DateTime.now!("Etc/UTC")})
|
|> Ash.Changeset.for_create(:create, %{
|
||||||
|
title: "aaa",
|
||||||
|
likes: 1,
|
||||||
|
arbitrary_timestamp: DateTime.now!("Etc/UTC")
|
||||||
|
})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "bbb", likes: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", likes: 1})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "aaa", likes: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", likes: 2})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load([:has_future_arbitrary_timestamp])
|
|> Ash.Query.load([:has_future_arbitrary_timestamp])
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "loading a calculation loads its dependent loads" do
|
test "loading a calculation loads its dependent loads" do
|
||||||
user =
|
user =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
account =
|
account =
|
||||||
Account
|
Account
|
||||||
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
||||||
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|> Api.load!([:active])
|
|> Ash.load!([:active])
|
||||||
|
|
||||||
assert account.active
|
assert account.active
|
||||||
end
|
end
|
||||||
|
@ -442,7 +446,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones",
|
last_name: "Jones",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
full_name_with_nils: "Bill Jones",
|
full_name_with_nils: "Bill Jones",
|
||||||
|
@ -452,7 +456,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load(:full_name_with_nils)
|
|> Ash.Query.load(:full_name_with_nils)
|
||||||
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with nil value" do
|
test "with nil value" do
|
||||||
|
@ -462,7 +466,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
first_name: "Bill",
|
first_name: "Bill",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
full_name_with_nils: "Bill",
|
full_name_with_nils: "Bill",
|
||||||
|
@ -472,23 +476,23 @@ defmodule AshPostgres.CalculationTest do
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load(:full_name_with_nils)
|
|> Ash.Query.load(:full_name_with_nils)
|
||||||
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "arguments with cast_in_query?: false are not cast" do
|
test "arguments with cast_in_query?: false are not cast" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "not", score: 42})
|
|> Ash.Changeset.for_create(:create, %{title: "not", score: 42})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [post] =
|
assert [post] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(similarity(search: expr(query(search: "match"))))
|
|> Ash.Query.filter(similarity(search: expr(query(search: "match"))))
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert post.title == "match"
|
assert post.title == "match"
|
||||||
end
|
end
|
||||||
|
@ -502,7 +506,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones",
|
last_name: "Jones",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
split_full_name: ["Bill", "Jones"]
|
split_full_name: ["Bill", "Jones"]
|
||||||
|
@ -510,7 +514,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load(:split_full_name)
|
|> Ash.Query.load(:split_full_name)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "trimming whitespace" do
|
test "trimming whitespace" do
|
||||||
|
@ -521,7 +525,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones ",
|
last_name: "Jones ",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
split_full_name_trim: ["Bill", "Jones"],
|
split_full_name_trim: ["Bill", "Jones"],
|
||||||
|
@ -530,37 +534,37 @@ defmodule AshPostgres.CalculationTest do
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load([:split_full_name_trim, :split_full_name])
|
|> Ash.Query.load([:split_full_name_trim, :split_full_name])
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "count_nils/1" do
|
describe "count_nils/1" do
|
||||||
test "counts nil values" do
|
test "counts nil values" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", nil, "c"]})
|
|> Ash.Changeset.for_create(:create, %{list_containing_nils: ["a", nil, "b", nil, "c"]})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", "c"]})
|
|> Ash.Changeset.for_create(:create, %{list_containing_nils: ["a", nil, "b", "c"]})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(count_nils(list_containing_nils) == 2)
|
|> Ash.Query.filter(count_nils(list_containing_nils) == 2)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "-/1" do
|
describe "-/1" do
|
||||||
test "makes numbers negative" do
|
test "makes numbers negative" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{negative_score: -42}] =
|
assert [%{negative_score: -42}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:negative_score)
|
|> Ash.Query.load(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -568,39 +572,39 @@ defmodule AshPostgres.CalculationTest do
|
||||||
test "maps can reference filtered aggregates" do
|
test "maps can reference filtered aggregates" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "foo", likes: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", likes: 2})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "foo", likes: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", likes: 2})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "bar", likes: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "bar", likes: 2})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{agg_map: %{called_foo: 2, called_bar: 1}}] =
|
assert [%{agg_map: %{called_foo: 2, called_bar: 1}}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:agg_map)
|
|> Ash.Query.load(:agg_map)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "maps can be constructed" do
|
test "maps can be constructed" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{score_map: %{negative_score: %{foo: -42}}}] =
|
assert [%{score_map: %{negative_score: %{foo: -42}}}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:score_map)
|
|> Ash.Query.load(:score_map)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -613,7 +617,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones ",
|
last_name: "Jones ",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
first_name_from_split: "Bill"
|
first_name_from_split: "Bill"
|
||||||
|
@ -621,15 +625,15 @@ defmodule AshPostgres.CalculationTest do
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(id == ^author.id)
|
|> Ash.Query.filter(id == ^author.id)
|
||||||
|> Ash.Query.load([:first_name_from_split])
|
|> Ash.Query.load([:first_name_from_split])
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "dependent calc" do
|
test "dependent calc" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", price: 10_024})
|
|> Ash.Changeset.for_create(:create, %{title: "match", price: 10_024})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post.get_by_id(post.id,
|
Post.get_by_id(post.id,
|
||||||
query: Post |> Ash.Query.select([:id]) |> Ash.Query.load([:price_string_with_currency_sign])
|
query: Post |> Ash.Query.select([:id]) |> Ash.Query.load([:price_string_with_currency_sign])
|
||||||
|
@ -639,10 +643,14 @@ defmodule AshPostgres.CalculationTest do
|
||||||
test "nested get_path works" do
|
test "nested get_path works" do
|
||||||
assert "thing" =
|
assert "thing" =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", price: 10_024, stuff: %{foo: %{bar: "thing"}}})
|
|> Ash.Changeset.for_create(:create, %{
|
||||||
|
title: "match",
|
||||||
|
price: 10_024,
|
||||||
|
stuff: %{foo: %{bar: "thing"}}
|
||||||
|
})
|
||||||
|> Ash.Changeset.deselect(:stuff)
|
|> Ash.Changeset.deselect(:stuff)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|> Api.load!(:foo_bar_from_stuff)
|
|> Ash.load!(:foo_bar_from_stuff)
|
||||||
|> Map.get(:foo_bar_from_stuff)
|
|> Map.get(:foo_bar_from_stuff)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -654,19 +662,19 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones",
|
last_name: "Jones",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %AshPostgres.Test.Money{} =
|
assert %AshPostgres.Test.Money{} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match", price: 10_024})
|
|> Ash.Changeset.for_create(:create, %{title: "match", price: 10_024})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|> Api.load!(:calc_returning_json)
|
|> Ash.load!(:calc_returning_json)
|
||||||
|> Map.get(:calc_returning_json)
|
|> Map.get(:calc_returning_json)
|
||||||
|
|
||||||
assert [%AshPostgres.Test.Money{}] =
|
assert [%AshPostgres.Test.Money{}] =
|
||||||
author
|
author
|
||||||
|> Api.load!(posts: :calc_returning_json)
|
|> Ash.load!(posts: :calc_returning_json)
|
||||||
|> Map.get(:posts)
|
|> Map.get(:posts)
|
||||||
|> Enum.map(&Map.get(&1, :calc_returning_json))
|
|> Enum.map(&Map.get(&1, :calc_returning_json))
|
||||||
end
|
end
|
||||||
|
@ -678,7 +686,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones",
|
last_name: "Jones",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{calculations: %{length: 9}} =
|
assert %{calculations: %{length: 9}} =
|
||||||
Author
|
Author
|
||||||
|
@ -687,7 +695,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
expr(string_length(string_trim(first_name <> last_name <> " "))),
|
expr(string_length(string_trim(first_name <> last_name <> " "))),
|
||||||
:integer
|
:integer
|
||||||
)
|
)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "an expression calculation that loads a runtime calculation works" do
|
test "an expression calculation that loads a runtime calculation works" do
|
||||||
|
@ -697,12 +705,12 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones",
|
last_name: "Jones",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{expr_referencing_runtime: "Bill Jones Bill Jones"}] =
|
assert [%{expr_referencing_runtime: "Bill Jones Bill Jones"}] =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.load(:expr_referencing_runtime)
|
|> Ash.Query.load(:expr_referencing_runtime)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "lazy values are evaluated lazily" do
|
test "lazy values are evaluated lazily" do
|
||||||
|
@ -712,7 +720,7 @@ defmodule AshPostgres.CalculationTest do
|
||||||
last_name: "Jones",
|
last_name: "Jones",
|
||||||
bio: %{title: "Mr.", bio: "Bones"}
|
bio: %{title: "Mr.", bio: "Bones"}
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{calculations: %{string: "fred"}} =
|
assert %{calculations: %{string: "fred"}} =
|
||||||
Author
|
Author
|
||||||
|
@ -721,21 +729,21 @@ defmodule AshPostgres.CalculationTest do
|
||||||
expr(lazy({__MODULE__, :fred, []})),
|
expr(lazy({__MODULE__, :fred, []})),
|
||||||
:string
|
:string
|
||||||
)
|
)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "exists with a relationship that has a filtered read action works" do
|
test "exists with a relationship that has a filtered read action works" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{})
|
|> Ash.Changeset.for_create(:create, %{})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post_id = post.id
|
post_id = post.id
|
||||||
|
|
||||||
assert [%{id: ^post_id}] =
|
assert [%{id: ^post_id}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(has_no_followers)
|
|> Ash.Query.filter(has_no_followers)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fred do
|
def fred do
|
||||||
|
|
|
@ -7,29 +7,29 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
certification =
|
certification =
|
||||||
AshPostgres.Test.ComplexCalculations.Certification
|
AshPostgres.Test.ComplexCalculations.Certification
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
skill =
|
skill =
|
||||||
AshPostgres.Test.ComplexCalculations.Skill
|
AshPostgres.Test.ComplexCalculations.Skill
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:certification, certification, type: :append)
|
|> Ash.Changeset.manage_relationship(:certification, certification, type: :append)
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
_documentation =
|
_documentation =
|
||||||
AshPostgres.Test.ComplexCalculations.Documentation
|
AshPostgres.Test.ComplexCalculations.Documentation
|
||||||
|> Ash.Changeset.new(%{status: :demonstrated})
|
|> Ash.Changeset.for_create(:create, %{status: :demonstrated})
|
||||||
|> Ash.Changeset.manage_relationship(:skill, skill, type: :append)
|
|> Ash.Changeset.manage_relationship(:skill, skill, type: :append)
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
skill =
|
skill =
|
||||||
skill
|
skill
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([:latest_documentation_status])
|
|> Ash.load!([:latest_documentation_status])
|
||||||
|
|
||||||
assert skill.latest_documentation_status == :demonstrated
|
assert skill.latest_documentation_status == :demonstrated
|
||||||
|
|
||||||
certification =
|
certification =
|
||||||
certification
|
certification
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
|> Ash.load!([
|
||||||
:count_of_skills
|
:count_of_skills
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
|
|
||||||
certification =
|
certification =
|
||||||
certification
|
certification
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
|> Ash.load!([
|
||||||
:count_of_approved_skills
|
:count_of_approved_skills
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
|
|
||||||
certification =
|
certification =
|
||||||
certification
|
certification
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
|> Ash.load!([
|
||||||
:count_of_documented_skills
|
:count_of_documented_skills
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
|
|
||||||
certification =
|
certification =
|
||||||
certification
|
certification
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
|> Ash.load!([
|
||||||
:count_of_documented_skills,
|
:count_of_documented_skills,
|
||||||
:all_documentation_approved,
|
:all_documentation_approved,
|
||||||
:some_documentation_created
|
:some_documentation_created
|
||||||
|
@ -66,35 +66,35 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
channel =
|
channel =
|
||||||
AshPostgres.Test.ComplexCalculations.Channel
|
AshPostgres.Test.ComplexCalculations.Channel
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user_1 =
|
user_1 =
|
||||||
AshPostgres.Test.User
|
AshPostgres.Test.User
|
||||||
|> Ash.Changeset.for_create(:create, %{name: "User 1"})
|
|> Ash.Changeset.for_create(:create, %{name: "User 1"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user_2 =
|
user_2 =
|
||||||
AshPostgres.Test.User
|
AshPostgres.Test.User
|
||||||
|> Ash.Changeset.for_create(:create, %{name: "User 2"})
|
|> Ash.Changeset.for_create(:create, %{name: "User 2"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
channel_member_1 =
|
channel_member_1 =
|
||||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:channel, channel, type: :append)
|
|> Ash.Changeset.manage_relationship(:channel, channel, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:user, user_1, type: :append)
|
|> Ash.Changeset.manage_relationship(:user, user_1, type: :append)
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
channel_member_2 =
|
channel_member_2 =
|
||||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:channel, channel, type: :append)
|
|> Ash.Changeset.manage_relationship(:channel, channel, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:user, user_2, type: :append)
|
|> Ash.Changeset.manage_relationship(:user, user_2, type: :append)
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
channel =
|
channel =
|
||||||
channel
|
channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
|> Ash.load!([
|
||||||
:first_member,
|
:first_member,
|
||||||
:second_member
|
:second_member
|
||||||
])
|
])
|
||||||
|
@ -104,13 +104,13 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
|
|
||||||
channel =
|
channel =
|
||||||
channel
|
channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!(:name, actor: user_1)
|
|> Ash.load!(:name, actor: user_1)
|
||||||
|
|
||||||
assert channel.name == user_1.name
|
assert channel.name == user_1.name
|
||||||
|
|
||||||
channel =
|
channel =
|
||||||
channel
|
channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!(:name, actor: user_2)
|
|> Ash.load!(:name, actor: user_2)
|
||||||
|
|
||||||
assert channel.name == user_2.name
|
assert channel.name == user_2.name
|
||||||
end
|
end
|
||||||
|
@ -119,31 +119,31 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
dm_channel =
|
dm_channel =
|
||||||
AshPostgres.Test.ComplexCalculations.DMChannel
|
AshPostgres.Test.ComplexCalculations.DMChannel
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user_1 =
|
user_1 =
|
||||||
AshPostgres.Test.User
|
AshPostgres.Test.User
|
||||||
|> Ash.Changeset.for_create(:create, %{name: "User 1"})
|
|> Ash.Changeset.for_create(:create, %{name: "User 1"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user_2 =
|
user_2 =
|
||||||
AshPostgres.Test.User
|
AshPostgres.Test.User
|
||||||
|> Ash.Changeset.for_create(:create, %{name: "User 2"})
|
|> Ash.Changeset.for_create(:create, %{name: "User 2"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
channel_member_1 =
|
channel_member_1 =
|
||||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||||
|> Ash.Changeset.for_create(:create, %{channel_id: dm_channel.id, user_id: user_1.id})
|
|> Ash.Changeset.for_create(:create, %{channel_id: dm_channel.id, user_id: user_1.id})
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
channel_member_2 =
|
channel_member_2 =
|
||||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||||
|> Ash.Changeset.for_create(:create, %{channel_id: dm_channel.id, user_id: user_2.id})
|
|> Ash.Changeset.for_create(:create, %{channel_id: dm_channel.id, user_id: user_2.id})
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
dm_channel =
|
dm_channel =
|
||||||
dm_channel
|
dm_channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
|> Ash.load!([
|
||||||
:first_member,
|
:first_member,
|
||||||
:second_member
|
:second_member
|
||||||
])
|
])
|
||||||
|
@ -153,24 +153,24 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
|
|
||||||
dm_channel =
|
dm_channel =
|
||||||
dm_channel
|
dm_channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!(:name, actor: user_1)
|
|> Ash.load!(:name, actor: user_1)
|
||||||
|
|
||||||
assert dm_channel.name == user_1.name
|
assert dm_channel.name == user_1.name
|
||||||
|
|
||||||
dm_channel =
|
dm_channel =
|
||||||
dm_channel
|
dm_channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!(:name, actor: user_2)
|
|> Ash.load!(:name, actor: user_2)
|
||||||
|
|
||||||
assert dm_channel.name == user_2.name
|
assert dm_channel.name == user_2.name
|
||||||
|
|
||||||
channels =
|
channels =
|
||||||
AshPostgres.Test.ComplexCalculations.Channel
|
AshPostgres.Test.ComplexCalculations.Channel
|
||||||
|> Ash.Query.for_read(:read)
|
|> Ash.Query.for_read(:read)
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
channels =
|
channels =
|
||||||
channels
|
channels
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([dm_channel: :name],
|
|> Ash.load!([dm_channel: :name],
|
||||||
actor: user_1
|
actor: user_1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
|
|
||||||
channel =
|
channel =
|
||||||
channel
|
channel
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([:dm_name, :foo], actor: user_2)
|
|> Ash.load!([:dm_name, :foo], actor: user_2)
|
||||||
|
|
||||||
assert channel.dm_name == user_2.name
|
assert channel.dm_name == user_2.name
|
||||||
end
|
end
|
||||||
|
@ -188,92 +188,92 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
||||||
test "calculations with parent filters can be filtered on themselves" do
|
test "calculations with parent filters can be filtered on themselves" do
|
||||||
AshPostgres.Test.ComplexCalculations.DMChannel
|
AshPostgres.Test.ComplexCalculations.DMChannel
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{foo: "foobar"}] =
|
assert [%{foo: "foobar"}] =
|
||||||
AshPostgres.Test.ComplexCalculations.Channel
|
AshPostgres.Test.ComplexCalculations.Channel
|
||||||
|> Ash.Query.filter(foo == "foobar")
|
|> Ash.Query.filter(foo == "foobar")
|
||||||
|> AshPostgres.Test.ComplexCalculations.Api.read!(load: :foo)
|
|> Ash.read!(load: :foo)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations with aggregates can be referenced from aggregates" do
|
test "calculations with aggregates can be referenced from aggregates" do
|
||||||
author =
|
author =
|
||||||
AshPostgres.Test.Author
|
AshPostgres.Test.Author
|
||||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{author_count_of_posts: 1}] =
|
assert [%{author_count_of_posts: 1}] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Query.load(:author_count_of_posts)
|
|> Ash.Query.load(:author_count_of_posts)
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%{author_count_of_posts: 1}] =
|
assert [%{author_count_of_posts: 1}] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
|> AshPostgres.Test.Api.load!(:author_count_of_posts)
|
|> Ash.load!(:author_count_of_posts)
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Query.filter(author_count_of_posts == 1)
|
|> Ash.Query.filter(author_count_of_posts == 1)
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can reference aggregates from optimizable first aggregates" do
|
test "calculations can reference aggregates from optimizable first aggregates" do
|
||||||
author =
|
author =
|
||||||
AshPostgres.Test.Author
|
AshPostgres.Test.Author
|
||||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{author_count_of_posts_agg: 1}] =
|
assert [%{author_count_of_posts_agg: 1}] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Query.load(:author_count_of_posts_agg)
|
|> Ash.Query.load(:author_count_of_posts_agg)
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%{author_count_of_posts_agg: 1}] =
|
assert [%{author_count_of_posts_agg: 1}] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
|> AshPostgres.Test.Api.load!(:author_count_of_posts_agg)
|
|> Ash.load!(:author_count_of_posts_agg)
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Query.filter(author_count_of_posts_agg == 1)
|
|> Ash.Query.filter(author_count_of_posts_agg == 1)
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can reference aggregates from non optimizable aggregates" do
|
test "calculations can reference aggregates from non optimizable aggregates" do
|
||||||
author =
|
author =
|
||||||
AshPostgres.Test.Author
|
AshPostgres.Test.Author
|
||||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{sum_of_author_count_of_posts: 1}] =
|
assert [%{sum_of_author_count_of_posts: 1}] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Query.load(:sum_of_author_count_of_posts)
|
|> Ash.Query.load(:sum_of_author_count_of_posts)
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%{sum_of_author_count_of_posts: 1}] =
|
assert [%{sum_of_author_count_of_posts: 1}] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
|> AshPostgres.Test.Api.load!(:sum_of_author_count_of_posts)
|
|> Ash.load!(:sum_of_author_count_of_posts)
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Query.filter(sum_of_author_count_of_posts == 1)
|
|> Ash.Query.filter(sum_of_author_count_of_posts == 1)
|
||||||
|> AshPostgres.Test.Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
defmodule AshPostgres.Test.CompositeTypeTest do
|
defmodule AshPostgres.Test.CompositeTypeTest do
|
||||||
use AshPostgres.RepoCase
|
use AshPostgres.RepoCase
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "can be cast and stored" do
|
test "can be cast and stored" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert post.composite_point.x == 1
|
assert post.composite_point.x == 1
|
||||||
assert post.composite_point.y == 2
|
assert post.composite_point.y == 2
|
||||||
|
@ -17,22 +17,22 @@ defmodule AshPostgres.Test.CompositeTypeTest do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post_id = post.id
|
post_id = post.id
|
||||||
|
|
||||||
assert %{id: ^post_id} = Post |> Ash.Query.filter(composite_point[:x] == 1) |> Api.read_one!()
|
assert %{id: ^post_id} = Post |> Ash.Query.filter(composite_point[:x] == 1) |> Ash.read_one!()
|
||||||
refute Post |> Ash.Query.filter(composite_point[:x] == 2) |> Api.read_one!()
|
refute Post |> Ash.Query.filter(composite_point[:x] == 2) |> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "composite types can be constructed" do
|
test "composite types can be constructed" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{composite_origin: %{x: 0, y: 0}} =
|
assert %{composite_origin: %{x: 0, y: 0}} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:composite_origin)
|
|> Ash.Query.load(:composite_origin)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
defmodule AshPostgres.ConstraintTest do
|
defmodule AshPostgres.ConstraintTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "constraint messages are properly raised" do
|
test "constraint messages are properly raised" do
|
||||||
assert_raise Ash.Error.Invalid, ~r/yo, bad price/, fn ->
|
assert_raise Ash.Error.Invalid, ~r/yo, bad price/, fn ->
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title", price: -1})
|
|> Ash.Changeset.for_create(:create, %{title: "title", price: -1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,24 +1,28 @@
|
||||||
defmodule AshPostgres.Test.CustomIndexTest do
|
defmodule AshPostgres.Test.CustomIndexTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "unique constraint errors are properly caught" do
|
test "unique constraint errors are properly caught" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "first", uniq_custom_one: "what", uniq_custom_two: "what2"})
|
|> Ash.Changeset.for_create(:create, %{
|
||||||
|> Api.create!()
|
title: "first",
|
||||||
|
uniq_custom_one: "what",
|
||||||
|
uniq_custom_two: "what2"
|
||||||
|
})
|
||||||
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Invalid,
|
assert_raise Ash.Error.Invalid,
|
||||||
~r/Invalid value provided for uniq_custom_one: dude what the heck/,
|
~r/Invalid value provided for uniq_custom_one: dude what the heck/,
|
||||||
fn ->
|
fn ->
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{
|
|> Ash.Changeset.for_create(:create, %{
|
||||||
title: "first",
|
title: "first",
|
||||||
uniq_custom_one: "what",
|
uniq_custom_one: "what",
|
||||||
uniq_custom_two: "what2"
|
uniq_custom_two: "what2"
|
||||||
})
|
})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
defmodule AshPostgres.DistinctTest do
|
defmodule AshPostgres.DistinctTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "title", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "title", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "foo", score: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", score: 2})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "foo", score: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "foo", score: 2})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
@ -30,7 +30,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.distinct(:title)
|
|> Ash.Query.distinct(:title)
|
||||||
|> Ash.Query.sort(:title)
|
|> Ash.Query.sort(:title)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%{title: "foo"}, %{title: "title"}] = results
|
assert [%{title: "foo"}, %{title: "title"}] = results
|
||||||
end
|
end
|
||||||
|
@ -40,7 +40,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.distinct(:title)
|
|> Ash.Query.distinct(:title)
|
||||||
|> Ash.Query.sort(title: :desc)
|
|> Ash.Query.sort(title: :desc)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%{title: "title"}, %{title: "foo"}] = results
|
assert [%{title: "title"}, %{title: "foo"}] = results
|
||||||
end
|
end
|
||||||
|
@ -51,7 +51,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct(:title)
|
|> Ash.Query.distinct(:title)
|
||||||
|> Ash.Query.sort(id: :desc)
|
|> Ash.Query.sort(id: :desc)
|
||||||
|> Ash.Query.limit(3)
|
|> Ash.Query.limit(3)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [_, _] = results
|
assert [_, _] = results
|
||||||
end
|
end
|
||||||
|
@ -62,7 +62,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct(:title)
|
|> Ash.Query.distinct(:title)
|
||||||
|> Ash.Query.sort(id: :desc)
|
|> Ash.Query.sort(id: :desc)
|
||||||
|> Ash.Query.limit(3)
|
|> Ash.Query.limit(3)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [_, _] = results
|
assert [_, _] = results
|
||||||
end
|
end
|
||||||
|
@ -73,7 +73,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct(:title)
|
|> Ash.Query.distinct(:title)
|
||||||
|> Ash.Query.sort(id: :desc)
|
|> Ash.Query.sort(id: :desc)
|
||||||
|> Ash.Query.limit(1)
|
|> Ash.Query.limit(1)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [_] = results
|
assert [_] = results
|
||||||
end
|
end
|
||||||
|
@ -84,7 +84,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct(:negative_score)
|
|> Ash.Query.distinct(:negative_score)
|
||||||
|> Ash.Query.sort(:negative_score)
|
|> Ash.Query.sort(:negative_score)
|
||||||
|> Ash.Query.load(:negative_score)
|
|> Ash.Query.load(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "foo", negative_score: -2},
|
%{title: "foo", negative_score: -2},
|
||||||
|
@ -96,7 +96,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct(:negative_score)
|
|> Ash.Query.distinct(:negative_score)
|
||||||
|> Ash.Query.sort(negative_score: :desc)
|
|> Ash.Query.sort(negative_score: :desc)
|
||||||
|> Ash.Query.load(:negative_score)
|
|> Ash.Query.load(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "title", negative_score: -1},
|
%{title: "title", negative_score: -1},
|
||||||
|
@ -108,7 +108,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct(:negative_score)
|
|> Ash.Query.distinct(:negative_score)
|
||||||
|> Ash.Query.sort(:title)
|
|> Ash.Query.sort(:title)
|
||||||
|> Ash.Query.load(:negative_score)
|
|> Ash.Query.load(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "foo", negative_score: -2},
|
%{title: "foo", negative_score: -2},
|
||||||
|
@ -118,29 +118,29 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|
|
||||||
test "distinct, join filters and sort can be combined" do
|
test "distinct, join filters and sort can be combined" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "a", score: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "a", score: 2})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "a", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "a", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.distinct(:negative_score)
|
|> Ash.Query.distinct(:negative_score)
|
||||||
|> Ash.Query.filter(author.first_name == "a")
|
|> Ash.Query.filter(author.first_name == "a")
|
||||||
|> Ash.Query.sort(:negative_score)
|
|> Ash.Query.sort(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "distinct sort is applied" do
|
test "distinct sort is applied" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "a", score: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "a", score: 2})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "a", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "a", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
results =
|
results =
|
||||||
Post
|
Post
|
||||||
|
@ -148,7 +148,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct_sort(:title)
|
|> Ash.Query.distinct_sort(:title)
|
||||||
|> Ash.Query.sort(:negative_score)
|
|> Ash.Query.sort(:negative_score)
|
||||||
|> Ash.Query.load(:negative_score)
|
|> Ash.Query.load(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "a", negative_score: -2},
|
%{title: "a", negative_score: -2},
|
||||||
|
@ -161,7 +161,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
|> Ash.Query.distinct_sort(title: :desc)
|
|> Ash.Query.distinct_sort(title: :desc)
|
||||||
|> Ash.Query.sort(:negative_score)
|
|> Ash.Query.sort(:negative_score)
|
||||||
|> Ash.Query.load(:negative_score)
|
|> Ash.Query.load(:negative_score)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "foo", negative_score: -2},
|
%{title: "foo", negative_score: -2},
|
||||||
|
@ -173,7 +173,7 @@ defmodule AshPostgres.DistinctTest do
|
||||||
results =
|
results =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.distinct(:title)
|
|> Ash.Query.distinct(:title)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [_, _] = results
|
assert [_, _] = results
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
defmodule AshPostgres.EmbeddableResourceTest do
|
defmodule AshPostgres.EmbeddableResourceTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Author, Bio, Post}
|
alias AshPostgres.Test.{Author, Bio, Post}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
%{post: post}
|
%{post: post}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can load json", %{post: post} do
|
test "calculations can load json", %{post: post} do
|
||||||
assert %{calc_returning_json: %AshPostgres.Test.Money{amount: 100, currency: :usd}} =
|
assert %{calc_returning_json: %AshPostgres.Test.Money{amount: 100, currency: :usd}} =
|
||||||
Api.load!(post, :calc_returning_json)
|
Ash.load!(post, :calc_returning_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "embeds with list attributes set to nil are loaded as nil" do
|
test "embeds with list attributes set to nil are loaded as nil" do
|
||||||
post =
|
author =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.new(%{bio: %Bio{list_of_strings: nil}})
|
|> Ash.Changeset.for_create(:create, %{bio: %Bio{list_of_strings: nil}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert is_nil(post.bio.list_of_strings)
|
assert is_nil(author.bio.list_of_strings)
|
||||||
|
|
||||||
post = Api.reload!(post)
|
author = Ash.reload!(author)
|
||||||
|
|
||||||
assert is_nil(post.bio.list_of_strings)
|
assert is_nil(author.bio.list_of_strings)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
defmodule AshPostgres.EnumTest do
|
defmodule AshPostgres.EnumTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "valid values are properly inserted" do
|
test "valid values are properly inserted" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title", status: :open})
|
|> Ash.Changeset.for_create(:create, %{title: "title", status: :open})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
defmodule AshPostgres.ErrorExprTest do
|
defmodule AshPostgres.ErrorExprTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
import Ash.Expr
|
import Ash.Expr
|
||||||
|
|
||||||
test "exceptions in filters are treated as regular Ash exceptions" do
|
test "exceptions in filters are treated as regular Ash exceptions" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(
|
|> Ash.Query.filter(
|
||||||
error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)
|
error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)
|
||||||
)
|
)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "exceptions in calculations are treated as regular Ash exceptions" do
|
test "exceptions in calculations are treated as regular Ash exceptions" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
||||||
Post
|
Post
|
||||||
|
@ -31,15 +31,15 @@ defmodule AshPostgres.ErrorExprTest do
|
||||||
expr(error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)),
|
expr(error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)),
|
||||||
:string
|
:string
|
||||||
)
|
)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.map(& &1.calculations)
|
|> Enum.map(& &1.calculations)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "exceptions in calculations are treated as regular Ash exceptions in transactions" do
|
test "exceptions in calculations are treated as regular Ash exceptions in transactions" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
||||||
AshPostgres.TestRepo.transaction!(fn ->
|
AshPostgres.TestRepo.transaction!(fn ->
|
||||||
|
@ -49,7 +49,7 @@ defmodule AshPostgres.ErrorExprTest do
|
||||||
expr(error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)),
|
expr(error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)),
|
||||||
:string
|
:string
|
||||||
)
|
)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|> Enum.map(& &1.calculations)
|
|> Enum.map(& &1.calculations)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
defmodule FilterFieldPolicyTest do
|
defmodule FilterFieldPolicyTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
|
|
||||||
alias AshPostgres.Test.{Api, Organization, Post, User}
|
alias AshPostgres.Test.{Organization, Post, User}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
|
@ -9,15 +9,15 @@ defmodule FilterFieldPolicyTest do
|
||||||
organization =
|
organization =
|
||||||
Organization
|
Organization
|
||||||
|> Ash.Changeset.for_create(:create, %{name: "test_org"})
|
|> Ash.Changeset.for_create(:create, %{name: "test_org"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.for_create(:create, %{organization_id: organization.id, name: "foo bar"})
|
|> Ash.Changeset.for_create(:create, %{organization_id: organization.id, name: "foo bar"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{organization_id: organization.id})
|
|> Ash.Changeset.for_create(:create, %{organization_id: organization.id})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
filter = Ash.Filter.parse_input!(Post, %{organization: %{name: %{ilike: "%org"}}})
|
filter = Ash.Filter.parse_input!(Post, %{organization: %{name: %{ilike: "%org"}}})
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ defmodule FilterFieldPolicyTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.do_filter(filter)
|
|> Ash.Query.do_filter(filter)
|
||||||
|> Ash.Query.for_read(:allow_any)
|
|> Ash.Query.for_read(:allow_any)
|
||||||
|> Api.read!(actor: %{id: "test"})
|
|> Ash.read!(actor: %{id: "test"})
|
||||||
|
|
||||||
filter = Ash.Filter.parse_input!(Post, %{organization: %{users: %{name: %{ilike: "%bar"}}}})
|
filter = Ash.Filter.parse_input!(Post, %{organization: %{users: %{name: %{ilike: "%bar"}}}})
|
||||||
|
|
||||||
|
@ -33,6 +33,6 @@ defmodule FilterFieldPolicyTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.do_filter(filter)
|
|> Ash.Query.do_filter(filter)
|
||||||
|> Ash.Query.for_read(:allow_any)
|
|> Ash.Query.for_read(:allow_any)
|
||||||
|> Api.read!(actor: %{id: "test"})
|
|> Ash.read!(actor: %{id: "test"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.Test.LoadTest do
|
defmodule AshPostgres.Test.LoadTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Comment, Post, Record, TempEntity}
|
alias AshPostgres.Test.{Comment, Post, Record, TempEntity}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
|
@ -8,18 +8,18 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
|
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
results =
|
results =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:comments)
|
|> Ash.Query.load(:comments)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%Post{comments: [%{title: "match"}]}] = results
|
assert [%Post{comments: [%{title: "match"}]}] = results
|
||||||
end
|
end
|
||||||
|
@ -28,18 +28,18 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
assert %Comment{post: %Ash.NotLoaded{type: :relationship}} =
|
assert %Comment{post: %Ash.NotLoaded{type: :relationship}} =
|
||||||
comment =
|
comment =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{})
|
|> Ash.Changeset.for_create(:create, %{})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> Ash.Changeset.manage_relationship(:comments, [comment], type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:comments, [comment], type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
results =
|
results =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.load(:post)
|
|> Ash.Query.load(:post)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%Comment{post: %{title: "match"}}] = results
|
assert [%Comment{post: %{title: "match"}}] = results
|
||||||
end
|
end
|
||||||
|
@ -47,29 +47,29 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
test "many_to_many loads work" do
|
test "many_to_many loads work" do
|
||||||
source_post =
|
source_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "source"})
|
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post =
|
destination_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "destination"})
|
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post2 =
|
destination_post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "destination"})
|
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
source_post
|
source_post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||||
type: :append_and_remove
|
type: :append_and_remove
|
||||||
)
|
)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
results =
|
results =
|
||||||
source_post
|
source_post
|
||||||
|> Api.load!(:linked_posts)
|
|> Ash.load!(:linked_posts)
|
||||||
|
|
||||||
assert %{linked_posts: [%{title: "destination"}, %{title: "destination"}]} = results
|
assert %{linked_posts: [%{title: "destination"}, %{title: "destination"}]} = results
|
||||||
end
|
end
|
||||||
|
@ -77,29 +77,29 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
test "many_to_many loads work when nested" do
|
test "many_to_many loads work when nested" do
|
||||||
source_post =
|
source_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "source"})
|
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post =
|
destination_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "destination"})
|
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
source_post
|
source_post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post],
|
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post],
|
||||||
type: :append_and_remove
|
type: :append_and_remove
|
||||||
)
|
)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
destination_post
|
destination_post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [source_post], type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:linked_posts, [source_post], type: :append_and_remove)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
results =
|
results =
|
||||||
source_post
|
source_post
|
||||||
|> Api.load!(linked_posts: :linked_posts)
|
|> Ash.load!(linked_posts: :linked_posts)
|
||||||
|
|
||||||
assert %{linked_posts: [%{title: "destination", linked_posts: [%{title: "source"}]}]} =
|
assert %{linked_posts: [%{title: "destination", linked_posts: [%{title: "source"}]}]} =
|
||||||
results
|
results
|
||||||
|
@ -109,75 +109,75 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
test "parent references are resolved" do
|
test "parent references are resolved" do
|
||||||
post1 =
|
post1 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2 =
|
post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2_id = post2.id
|
post2_id = post2.id
|
||||||
|
|
||||||
post3 =
|
post3 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{posts_with_matching_title: [%{id: ^post2_id}]}] =
|
assert [%{posts_with_matching_title: [%{id: ^post2_id}]}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:posts_with_matching_title)
|
|> Ash.Query.load(:posts_with_matching_title)
|
||||||
|> Ash.Query.filter(id == ^post1.id)
|
|> Ash.Query.filter(id == ^post1.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%{posts_with_matching_title: []}] =
|
assert [%{posts_with_matching_title: []}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:posts_with_matching_title)
|
|> Ash.Query.load(:posts_with_matching_title)
|
||||||
|> Ash.Query.filter(id == ^post3.id)
|
|> Ash.Query.filter(id == ^post3.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "parent references work when joining for filters" do
|
test "parent references work when joining for filters" do
|
||||||
%{id: post1_id} =
|
%{id: post1_id} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2 =
|
post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{id: ^post1_id}] =
|
assert [%{id: ^post1_id}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(posts_with_matching_title.id == ^post2.id)
|
|> Ash.Query.filter(posts_with_matching_title.id == ^post2.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "lateral join loads (loads with limits or offsets) are supported" do
|
test "lateral join loads (loads with limits or offsets) are supported" do
|
||||||
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
|
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "abc"})
|
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "def"})
|
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
comments_query =
|
comments_query =
|
||||||
Comment
|
Comment
|
||||||
|
@ -187,7 +187,7 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
results =
|
results =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(comments: comments_query)
|
|> Ash.Query.load(comments: comments_query)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%Post{comments: [%{title: "abc"}]}] = results
|
assert [%Post{comments: [%{title: "abc"}]}] = results
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
results =
|
results =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(comments: comments_query)
|
|> Ash.Query.load(comments: comments_query)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%Post{comments: [%{title: "def"}]}] = results
|
assert [%Post{comments: [%{title: "def"}]}] = results
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
results =
|
results =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(comments: comments_query)
|
|> Ash.Query.load(comments: comments_query)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [%Post{comments: [%{title: "def"}, %{title: "abc"}]}] = results
|
assert [%Post{comments: [%{title: "def"}, %{title: "abc"}]}] = results
|
||||||
end
|
end
|
||||||
|
@ -219,25 +219,25 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
test "loading many to many relationships on records works without loading its join relationship when using code interface" do
|
test "loading many to many relationships on records works without loading its join relationship when using code interface" do
|
||||||
source_post =
|
source_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "source"})
|
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post =
|
destination_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "abc"})
|
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post2 =
|
destination_post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "def"})
|
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
source_post
|
source_post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||||
type: :append_and_remove
|
type: :append_and_remove
|
||||||
)
|
)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
assert %{linked_posts: [_, _]} = Post.get_by_id!(source_post.id, load: [:linked_posts])
|
assert %{linked_posts: [_, _]} = Post.get_by_id!(source_post.id, load: [:linked_posts])
|
||||||
end
|
end
|
||||||
|
@ -245,25 +245,25 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
test "lateral join loads with many to many relationships are supported" do
|
test "lateral join loads with many to many relationships are supported" do
|
||||||
source_post =
|
source_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "source"})
|
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post =
|
destination_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "abc"})
|
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post2 =
|
destination_post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "def"})
|
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
source_post
|
source_post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||||
type: :append_and_remove
|
type: :append_and_remove
|
||||||
)
|
)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
linked_posts_query =
|
linked_posts_query =
|
||||||
Post
|
Post
|
||||||
|
@ -272,7 +272,7 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
|
|
||||||
results =
|
results =
|
||||||
source_post
|
source_post
|
||||||
|> Api.load!(linked_posts: linked_posts_query)
|
|> Ash.load!(linked_posts: linked_posts_query)
|
||||||
|
|
||||||
assert %{linked_posts: [%{title: "abc"}]} = results
|
assert %{linked_posts: [%{title: "abc"}]} = results
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
|
|
||||||
results =
|
results =
|
||||||
source_post
|
source_post
|
||||||
|> Api.load!(linked_posts: linked_posts_query)
|
|> Ash.load!(linked_posts: linked_posts_query)
|
||||||
|
|
||||||
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
|
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
|
||||||
end
|
end
|
||||||
|
@ -291,25 +291,25 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
test "lateral join loads with many to many relationships are supported with aggregates" do
|
test "lateral join loads with many to many relationships are supported with aggregates" do
|
||||||
source_post =
|
source_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "source"})
|
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post =
|
destination_post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "abc"})
|
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
destination_post2 =
|
destination_post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "def"})
|
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
source_post
|
source_post
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||||
type: :append_and_remove
|
type: :append_and_remove
|
||||||
)
|
)
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
linked_posts_query =
|
linked_posts_query =
|
||||||
Post
|
Post
|
||||||
|
@ -318,7 +318,7 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
|
|
||||||
results =
|
results =
|
||||||
source_post
|
source_post
|
||||||
|> Api.load!(linked_posts: linked_posts_query)
|
|> Ash.load!(linked_posts: linked_posts_query)
|
||||||
|
|
||||||
assert %{linked_posts: [%{title: "abc"}]} = results
|
assert %{linked_posts: [%{title: "abc"}]} = results
|
||||||
|
|
||||||
|
@ -330,16 +330,18 @@ defmodule AshPostgres.Test.LoadTest do
|
||||||
|
|
||||||
results =
|
results =
|
||||||
source_post
|
source_post
|
||||||
|> Api.load!(linked_posts: linked_posts_query)
|
|> Ash.load!(linked_posts: linked_posts_query)
|
||||||
|
|
||||||
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
|
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
|
||||||
end
|
end
|
||||||
|
|
||||||
test "lateral join loads with read action from a custom table and schema" do
|
test "lateral join loads with read action from a custom table and schema" do
|
||||||
record = Record |> Ash.Changeset.new(%{full_name: "name"}) |> Api.create!()
|
record = Record |> Ash.Changeset.for_create(:create, %{full_name: "name"}) |> Ash.create!()
|
||||||
temp_entity = TempEntity |> Ash.Changeset.new(%{full_name: "name"}) |> Api.create!()
|
|
||||||
|
|
||||||
assert %{entity: entity} = Api.load!(record, :entity)
|
temp_entity =
|
||||||
|
TempEntity |> Ash.Changeset.for_create(:create, %{full_name: "name"}) |> Ash.create!()
|
||||||
|
|
||||||
|
assert %{entity: entity} = Ash.load!(record, :entity)
|
||||||
|
|
||||||
assert temp_entity.id == entity.id
|
assert temp_entity.id == entity.id
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.Test.LockTest do
|
defmodule AshPostgres.Test.LockTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
|
@ -17,7 +17,7 @@ defmodule AshPostgres.Test.LockTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{title: "locked"})
|
|> Ash.Changeset.for_create(:create, %{title: "locked"})
|
||||||
|> Ash.Changeset.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
|> Ash.Changeset.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
task1 =
|
task1 =
|
||||||
Task.async(fn ->
|
Task.async(fn ->
|
||||||
|
@ -26,7 +26,7 @@ defmodule AshPostgres.Test.LockTest do
|
||||||
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|
||||||
|> Ash.Query.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
|> Ash.Query.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
||||||
|> Ash.Query.filter(id == ^post.id)
|
|> Ash.Query.filter(id == ^post.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
:timer.sleep(1000)
|
:timer.sleep(1000)
|
||||||
:ok
|
:ok
|
||||||
|
@ -43,7 +43,7 @@ defmodule AshPostgres.Test.LockTest do
|
||||||
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|
||||||
|> Ash.Query.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
|> Ash.Query.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
||||||
|> Ash.Query.filter(id == ^post.id)
|
|> Ash.Query.filter(id == ^post.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end)
|
end)
|
||||||
rescue
|
rescue
|
||||||
e ->
|
e ->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.Test.ManualRelationshipsTest do
|
defmodule AshPostgres.Test.ManualRelationshipsTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Comment, Post}
|
alias AshPostgres.Test.{Comment, Post}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
|
@ -8,102 +8,102 @@ defmodule AshPostgres.Test.ManualRelationshipsTest do
|
||||||
test "aggregates can be loaded with no data" do
|
test "aggregates can be loaded with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{count_of_comments_containing_title: 0} =
|
assert %{count_of_comments_containing_title: 0} =
|
||||||
Api.load!(post, :count_of_comments_containing_title)
|
Ash.load!(post, :count_of_comments_containing_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be loaded with data" do
|
test "aggregates can be loaded with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{count_of_comments_containing_title: 1} =
|
assert %{count_of_comments_containing_title: 1} =
|
||||||
Api.load!(post, :count_of_comments_containing_title)
|
Ash.load!(post, :count_of_comments_containing_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "relationships can be filtered on with no data" do
|
test "relationships can be filtered on with no data" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Post |> Ash.Query.filter(comments_containing_title.title == "title") |> Api.read!()
|
Post |> Ash.Query.filter(comments_containing_title.title == "title") |> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be filtered on with no data" do
|
test "aggregates can be filtered on with no data" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] = Post |> Ash.Query.filter(count_of_comments_containing_title == 1) |> Api.read!()
|
assert [] = Post |> Ash.Query.filter(count_of_comments_containing_title == 1) |> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be filtered on with data" do
|
test "aggregates can be filtered on with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
Post |> Ash.Query.filter(count_of_comments_containing_title == 1) |> Api.read!()
|
Post |> Ash.Query.filter(count_of_comments_containing_title == 1) |> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "relationships can be filtered on with data" do
|
test "relationships can be filtered on with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.filter(comments_containing_title.title == "title2")
|
|> Ash.Query.filter(comments_containing_title.title == "title2")
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -111,128 +111,128 @@ defmodule AshPostgres.Test.ManualRelationshipsTest do
|
||||||
test "aggregates can be loaded with no data" do
|
test "aggregates can be loaded with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
comment =
|
comment =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{count_of_comments_containing_title: 0} =
|
assert %{count_of_comments_containing_title: 0} =
|
||||||
Api.load!(comment, :count_of_comments_containing_title)
|
Ash.load!(comment, :count_of_comments_containing_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be loaded with data" do
|
test "aggregates can be loaded with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
comment =
|
comment =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{count_of_comments_containing_title: 1} =
|
assert %{count_of_comments_containing_title: 1} =
|
||||||
Api.load!(comment, :count_of_comments_containing_title)
|
Ash.load!(comment, :count_of_comments_containing_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be filtered on with no data" do
|
test "aggregates can be filtered on with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(count_of_comments_containing_title == 1)
|
|> Ash.Query.filter(count_of_comments_containing_title == 1)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "relationships can be filtered on with no data" do
|
test "relationships can be filtered on with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(post.comments_containing_title.title == "title2")
|
|> Ash.Query.filter(post.comments_containing_title.title == "title2")
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be filtered on with data" do
|
test "aggregates can be filtered on with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_, _] =
|
assert [_, _] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(count_of_comments_containing_title == 1)
|
|> Ash.Query.filter(count_of_comments_containing_title == 1)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "relationships can be filtered on with data" do
|
test "relationships can be filtered on with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_, _] =
|
assert [_, _] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(post.comments_containing_title.title == "title2")
|
|> Ash.Query.filter(post.comments_containing_title.title == "title2")
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -240,128 +240,128 @@ defmodule AshPostgres.Test.ManualRelationshipsTest do
|
||||||
test "aggregates can be loaded with no data" do
|
test "aggregates can be loaded with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
comment =
|
comment =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{posts_for_comments_containing_title: []} =
|
assert %{posts_for_comments_containing_title: []} =
|
||||||
Api.load!(comment, :posts_for_comments_containing_title)
|
Ash.load!(comment, :posts_for_comments_containing_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be loaded with data" do
|
test "aggregates can be loaded with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
comment =
|
comment =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{posts_for_comments_containing_title: ["title"]} =
|
assert %{posts_for_comments_containing_title: ["title"]} =
|
||||||
Api.load!(comment, :posts_for_comments_containing_title)
|
Ash.load!(comment, :posts_for_comments_containing_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be filtered on with no data" do
|
test "aggregates can be filtered on with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter("title" in posts_for_comments_containing_title)
|
|> Ash.Query.filter("title" in posts_for_comments_containing_title)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "relationships can be filtered on with no data" do
|
test "relationships can be filtered on with no data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [] =
|
assert [] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates can be filtered on with data" do
|
test "aggregates can be filtered on with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_, _] =
|
assert [_, _] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "relationships can be filtered on with data" do
|
test "relationships can be filtered on with data" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "title2"})
|
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "no match"})
|
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_, _] =
|
assert [_, _] =
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,18 +4,18 @@ defmodule AshPostgres.ManualUpdateTest do
|
||||||
test "Manual update defined in a module to update an attribute" do
|
test "Manual update defined in a module to update an attribute" do
|
||||||
post =
|
post =
|
||||||
AshPostgres.Test.Post
|
AshPostgres.Test.Post
|
||||||
|> Ash.Changeset.new(%{title: "match"})
|
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
AshPostgres.Test.Comment
|
AshPostgres.Test.Comment
|
||||||
|> Ash.Changeset.new(%{title: "_"})
|
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> AshPostgres.Test.Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post =
|
post =
|
||||||
post
|
post
|
||||||
|> Ash.Changeset.for_update(:manual_update)
|
|> Ash.Changeset.for_update(:manual_update)
|
||||||
|> AshPostgres.Test.Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
assert post.title == "manual"
|
assert post.title == "manual"
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,18 @@
|
||||||
defmodule AshPostgres.Test.MultitenancyTest do
|
defmodule AshPostgres.Test.MultitenancyTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
|
|
||||||
alias AshPostgres.MultitenancyTest.{Api, Org, Post, User}
|
alias AshPostgres.MultitenancyTest.{Org, Post, User}
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
org1 =
|
org1 =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new(name: "test1")
|
|> Ash.Changeset.for_create(:create, %{name: "test1"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
org2 =
|
org2 =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new(name: "test2")
|
|> Ash.Changeset.for_create(:create, %{name: "test2"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
[org1: org1, org2: org2]
|
[org1: org1, org2: org2]
|
||||||
end
|
end
|
||||||
|
@ -34,17 +34,17 @@ defmodule AshPostgres.Test.MultitenancyTest do
|
||||||
assert [%{id: ^org_id}] =
|
assert [%{id: ^org_id}] =
|
||||||
Org
|
Org
|
||||||
|> Ash.Query.set_tenant(tenant(org1))
|
|> Ash.Query.set_tenant(tenant(org1))
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "context multitenancy works with policies", %{org1: org1} do
|
test "context multitenancy works with policies", %{org1: org1} do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(name: "foo")
|
|> Ash.Changeset.for_create(:create, %{name: "foo"})
|
||||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|> Ash.Changeset.for_update(:update_with_policy, %{}, authorize?: true)
|
|> Ash.Changeset.for_update(:update_with_policy, %{}, authorize?: true)
|
||||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "attribute multitenancy is set on creation" do
|
test "attribute multitenancy is set on creation" do
|
||||||
|
@ -52,29 +52,29 @@ defmodule AshPostgres.Test.MultitenancyTest do
|
||||||
|
|
||||||
org =
|
org =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new(name: "test3")
|
|> Ash.Changeset.for_create(:create, %{name: "test3"})
|
||||||
|> Ash.Changeset.set_tenant("org_#{uuid}")
|
|> Ash.Changeset.set_tenant("org_#{uuid}")
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert org.id == uuid
|
assert org.id == uuid
|
||||||
end
|
end
|
||||||
|
|
||||||
test "schema multitenancy works", %{org1: org1, org2: org2} do
|
test "schema multitenancy works", %{org1: org1, org2: org2} do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(name: "foo")
|
|> Ash.Changeset.for_create(:create, %{name: "foo"})
|
||||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [_] = Post |> Ash.Query.set_tenant(tenant(org1)) |> Api.read!()
|
assert [_] = Post |> Ash.Query.set_tenant(tenant(org1)) |> Ash.read!()
|
||||||
assert [] = Post |> Ash.Query.set_tenant(tenant(org2)) |> Api.read!()
|
assert [] = Post |> Ash.Query.set_tenant(tenant(org2)) |> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "schema rename on update works", %{org1: org1} do
|
test "schema rename on update works", %{org1: org1} do
|
||||||
new_uuid = Ash.UUID.generate()
|
new_uuid = Ash.UUID.generate()
|
||||||
|
|
||||||
org1
|
org1
|
||||||
|> Ash.Changeset.new(id: new_uuid)
|
|> Ash.Changeset.for_update(:update, %{id: new_uuid})
|
||||||
|> Api.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
new_tenant = "org_#{new_uuid}"
|
new_tenant = "org_#{new_uuid}"
|
||||||
|
|
||||||
|
@ -91,106 +91,106 @@ defmodule AshPostgres.Test.MultitenancyTest do
|
||||||
org =
|
org =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user =
|
user =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert Api.load!(user, :org).org.id == org.id
|
assert Ash.load!(user, :org).org.id == org.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "loading context multitenant resources from attribute multitenant resources works" do
|
test "loading context multitenant resources from attribute multitenant resources works" do
|
||||||
org =
|
org =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user1 =
|
user1 =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.new(%{name: "a"})
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
||||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user2 =
|
user2 =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.new(%{name: "b"})
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
||||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user1_id = user1.id
|
user1_id = user1.id
|
||||||
user2_id = user2.id
|
user2_id = user2.id
|
||||||
|
|
||||||
assert [%{id: ^user1_id}, %{id: ^user2_id}] =
|
assert [%{id: ^user1_id}, %{id: ^user2_id}] =
|
||||||
Api.load!(org, users: Ash.Query.sort(User, :name)).users
|
Ash.load!(org, users: Ash.Query.sort(User, :name)).users
|
||||||
end
|
end
|
||||||
|
|
||||||
test "manage_relationship from context multitenant resource to attribute multitenant resource doesn't raise an error" do
|
test "manage_relationship from context multitenant resource to attribute multitenant resource doesn't raise an error" do
|
||||||
org = Org |> Ash.Changeset.new() |> Api.create!()
|
org = Org |> Ash.Changeset.new() |> Ash.create!()
|
||||||
user = User |> Ash.Changeset.new() |> Api.create!()
|
user = User |> Ash.Changeset.new() |> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, %{}, tenant: tenant(org))
|
|> Ash.Changeset.for_create(:create, %{}, tenant: tenant(org))
|
||||||
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "loading attribute multitenant resources with limits from context multitenant resources works" do
|
test "loading attribute multitenant resources with limits from context multitenant resources works" do
|
||||||
org =
|
org =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user =
|
user =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert Api.load!(user, :org).org.id == org.id
|
assert Ash.load!(user, :org).org.id == org.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "loading context multitenant resources with limits from attribute multitenant resources works" do
|
test "loading context multitenant resources with limits from attribute multitenant resources works" do
|
||||||
org =
|
org =
|
||||||
Org
|
Org
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user1 =
|
user1 =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.new(%{name: "a"})
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
||||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user2 =
|
user2 =
|
||||||
User
|
User
|
||||||
|> Ash.Changeset.new(%{name: "b"})
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
||||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
user1_id = user1.id
|
user1_id = user1.id
|
||||||
user2_id = user2.id
|
user2_id = user2.id
|
||||||
|
|
||||||
assert [%{id: ^user1_id}, %{id: ^user2_id}] =
|
assert [%{id: ^user1_id}, %{id: ^user2_id}] =
|
||||||
Api.load!(org, users: Ash.Query.sort(Ash.Query.limit(User, 10), :name)).users
|
Ash.load!(org, users: Ash.Query.sort(Ash.Query.limit(User, 10), :name)).users
|
||||||
end
|
end
|
||||||
|
|
||||||
test "unique constraints are properly scoped", %{org1: org1} do
|
test "unique constraints are properly scoped", %{org1: org1} do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{})
|
|> Ash.Changeset.for_create(:create, %{})
|
||||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert_raise Ash.Error.Invalid,
|
assert_raise Ash.Error.Invalid,
|
||||||
~r/Invalid value provided for id: has already been taken/,
|
~r/Invalid value provided for id: has already been taken/,
|
||||||
fn ->
|
fn ->
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{id: post.id})
|
|> Ash.Changeset.for_create(:create, %{id: post.id})
|
||||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
defmodule AshPostgres.PolymorphismTest do
|
defmodule AshPostgres.PolymorphismTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post, Rating}
|
alias AshPostgres.Test.{Post, Rating}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "you can create related data" do
|
test "you can create related data" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, rating: %{score: 10})
|
|> Ash.Changeset.for_create(:create, rating: %{score: 10})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{score: 10}] =
|
assert [%{score: 10}] =
|
||||||
Rating
|
Rating
|
||||||
|> Ash.Query.set_context(%{data_layer: %{table: "post_ratings"}})
|
|> Ash.Query.set_context(%{data_layer: %{table: "post_ratings"}})
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "you can read related data" do
|
test "you can read related data" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_create(:create, rating: %{score: 10})
|
|> Ash.Changeset.for_create(:create, rating: %{score: 10})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{score: 10}] =
|
assert [%{score: 10}] =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:ratings)
|
|> Ash.Query.load(:ratings)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|> Map.get(:ratings)
|
|> Map.get(:ratings)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
defmodule AshPostgres.Test.PrimaryKeyTest do
|
defmodule AshPostgres.Test.PrimaryKeyTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, IntegerPost, Post, PostView}
|
alias AshPostgres.Test.{IntegerPost, Post, PostView}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "creates record with integer primary key" do
|
test "creates record with integer primary key" do
|
||||||
assert %IntegerPost{} = IntegerPost |> Ash.Changeset.new(%{title: "title"}) |> Api.create!()
|
assert %IntegerPost{} =
|
||||||
|
IntegerPost |> Ash.Changeset.for_create(:create, %{title: "title"}) |> Ash.create!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creates record with uuid primary key" do
|
test "creates record with uuid primary key" do
|
||||||
assert %Post{} = Post |> Ash.Changeset.new(%{title: "title"}) |> Api.create!()
|
assert %Post{} = Post |> Ash.Changeset.for_create(:create, %{title: "title"}) |> Ash.create!()
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "resources without a primary key" do
|
describe "resources without a primary key" do
|
||||||
|
@ -18,12 +19,12 @@ defmodule AshPostgres.Test.PrimaryKeyTest do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_action(:create, %{title: "not very interesting"})
|
|> Ash.Changeset.for_action(:create, %{title: "not very interesting"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert {:ok, view} =
|
assert {:ok, view} =
|
||||||
PostView
|
PostView
|
||||||
|> Ash.Changeset.for_action(:create, %{browser: :firefox, post_id: post.id})
|
|> Ash.Changeset.for_action(:create, %{browser: :firefox, post_id: post.id})
|
||||||
|> Api.create()
|
|> Ash.create()
|
||||||
|
|
||||||
assert view.browser == :firefox
|
assert view.browser == :firefox
|
||||||
assert view.post_id == post.id
|
assert view.post_id == post.id
|
||||||
|
@ -34,14 +35,14 @@ defmodule AshPostgres.Test.PrimaryKeyTest do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.for_action(:create, %{title: "not very interesting"})
|
|> Ash.Changeset.for_action(:create, %{title: "not very interesting"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
expected =
|
expected =
|
||||||
PostView
|
PostView
|
||||||
|> Ash.Changeset.for_action(:create, %{browser: :firefox, post_id: post.id})
|
|> Ash.Changeset.for_action(:create, %{browser: :firefox, post_id: post.id})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert {:ok, [actual]} = Api.read(PostView)
|
assert {:ok, [actual]} = Ash.read(PostView)
|
||||||
|
|
||||||
assert actual.time == expected.time
|
assert actual.time == expected.time
|
||||||
assert actual.browser == expected.browser
|
assert actual.browser == expected.browser
|
||||||
|
|
|
@ -8,11 +8,12 @@ defmodule AshPostgres.ReferencesTest do
|
||||||
defmodule Org do
|
defmodule Org do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: nil,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
multitenancy do
|
multitenancy do
|
||||||
|
@ -33,14 +34,15 @@ defmodule AshPostgres.ReferencesTest do
|
||||||
defmodule User do
|
defmodule User do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: nil,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:secondary_id, :uuid)
|
attribute(:secondary_id, :uuid, public?: true)
|
||||||
attribute(:foo_id, :uuid)
|
attribute(:foo_id, :uuid, public?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
attribute(:org_id, :uuid)
|
attribute(:org_id, :uuid, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
multitenancy do
|
multitenancy do
|
||||||
|
@ -49,7 +51,9 @@ defmodule AshPostgres.ReferencesTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:org, Org)
|
belongs_to(:org, Org) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -66,13 +70,14 @@ defmodule AshPostgres.ReferencesTest do
|
||||||
defmodule UserThing do
|
defmodule UserThing do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: nil,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
attribute(:id, :string, primary_key?: true, allow_nil?: false)
|
attribute(:id, :string, primary_key?: true, allow_nil?: false, public?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
attribute(:org_id, :uuid)
|
attribute(:org_id, :uuid, public?: true)
|
||||||
attribute(:foo_id, :uuid)
|
attribute(:foo_id, :uuid, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
multitenancy do
|
multitenancy do
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
defmodule AshPostgres.RelWithParentFilterTest do
|
defmodule AshPostgres.RelWithParentFilterTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
|
|
||||||
alias AshPostgres.Test.{Api, Author}
|
alias AshPostgres.Test.Author
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
|
@ -9,11 +9,11 @@ defmodule AshPostgres.RelWithParentFilterTest do
|
||||||
%{id: author_id} =
|
%{id: author_id} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.for_create(:create, %{first_name: "John", last_name: "Doe"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "John", last_name: "Doe"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.for_create(:create, %{first_name: "John"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "John"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
# here we get the expected result of 1 because it is done in the same query
|
# here we get the expected result of 1 because it is done in the same query
|
||||||
assert %{num_of_authors_with_same_first_name: 1} =
|
assert %{num_of_authors_with_same_first_name: 1} =
|
||||||
|
@ -21,18 +21,18 @@ defmodule AshPostgres.RelWithParentFilterTest do
|
||||||
|> Ash.Query.for_read(:read)
|
|> Ash.Query.for_read(:read)
|
||||||
|> Ash.Query.filter(id == ^author_id)
|
|> Ash.Query.filter(id == ^author_id)
|
||||||
|> Ash.Query.load(:num_of_authors_with_same_first_name)
|
|> Ash.Query.load(:num_of_authors_with_same_first_name)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "filter on relationship using parent works as expected when loading relationship" do
|
test "filter on relationship using parent works as expected when loading relationship" do
|
||||||
%{id: author_id} =
|
%{id: author_id} =
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.for_create(:create, %{first_name: "John", last_name: "Doe"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "John", last_name: "Doe"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Author
|
Author
|
||||||
|> Ash.Changeset.for_create(:create, %{first_name: "John"})
|
|> Ash.Changeset.for_create(:create, %{first_name: "John"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert %{authors_with_same_first_name: authors} =
|
assert %{authors_with_same_first_name: authors} =
|
||||||
Author
|
Author
|
||||||
|
@ -43,7 +43,7 @@ defmodule AshPostgres.RelWithParentFilterTest do
|
||||||
# but when doing that it does a inner lateral join
|
# but when doing that it does a inner lateral join
|
||||||
# instead of using the id from the parent relationship
|
# instead of using the id from the parent relationship
|
||||||
|> Ash.Query.load(:authors_with_same_first_name)
|
|> Ash.Query.load(:authors_with_same_first_name)
|
||||||
|> Api.read_one!()
|
|> Ash.read_one!()
|
||||||
|
|
||||||
assert length(authors) == 1
|
assert length(authors) == 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
defmodule AshPostgres.SchemaTest do
|
defmodule AshPostgres.SchemaTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Author, Profile}
|
alias AshPostgres.Test.{Author, Profile}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
[author: Api.create!(Ash.Changeset.for_create(Author, :create, %{}))]
|
[author: Ash.create!(Ash.Changeset.for_create(Author, :create, %{}))]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "data can be created", %{author: author} do
|
test "data can be created", %{author: author} do
|
||||||
|
@ -14,16 +14,16 @@ defmodule AshPostgres.SchemaTest do
|
||||||
Profile
|
Profile
|
||||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "data can be read", %{author: author} do
|
test "data can be read", %{author: author} do
|
||||||
Profile
|
Profile
|
||||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{description: "foo"}] = Profile |> Api.read!()
|
assert [%{description: "foo"}] = Profile |> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "they can be filtered across", %{author: author} do
|
test "they can be filtered across", %{author: author} do
|
||||||
|
@ -31,33 +31,33 @@ defmodule AshPostgres.SchemaTest do
|
||||||
Profile
|
Profile
|
||||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Api.create!(Ash.Changeset.for_create(Author, :create, %{}))
|
Ash.create!(Ash.Changeset.for_create(Author, :create, %{}))
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(profile.id == ^profile.id)
|
|> Ash.Query.filter(profile.id == ^profile.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [_] =
|
assert [_] =
|
||||||
Profile
|
Profile
|
||||||
|> Ash.Query.filter(author.id == ^author.id)
|
|> Ash.Query.filter(author.id == ^author.id)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "aggregates work across schemas", %{author: author} do
|
test "aggregates work across schemas", %{author: author} do
|
||||||
Profile
|
Profile
|
||||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{profile_description: "foo"}] =
|
assert [%{profile_description: "foo"}] =
|
||||||
Author
|
Author
|
||||||
|> Ash.Query.filter(profile_description == "foo")
|
|> Ash.Query.filter(profile_description == "foo")
|
||||||
|> Ash.Query.load(:profile_description)
|
|> Ash.Query.load(:profile_description)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert %{profile_description: "foo"} = Api.load!(author, :profile_description)
|
assert %{profile_description: "foo"} = Ash.load!(author, :profile_description)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
defmodule AshPostgres.SelectTest do
|
defmodule AshPostgres.SelectTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Post}
|
alias AshPostgres.Test.Post
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
test "values not selected in the query are not present in the response" do
|
test "values not selected in the query are not present in the response" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "title"})
|
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [%{title: nil}] = Api.read!(Ash.Query.select(Post, :id))
|
assert [%{title: %Ash.NotLoaded{}}] = Ash.read!(Ash.Query.select(Post, :id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
defmodule AshPostgres.SortTest do
|
defmodule AshPostgres.SortTest do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Api, Comment, Post, PostLink}
|
alias AshPostgres.Test.{Comment, Post, PostLink}
|
||||||
|
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
require Ash.Sort
|
require Ash.Sort
|
||||||
|
|
||||||
test "multi-column sorts work" do
|
test "multi-column sorts work" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "aaa", score: 0},
|
%{title: "aaa", score: 0},
|
||||||
%{title: "aaa", score: 1},
|
%{title: "aaa", score: 1},
|
||||||
%{title: "bbb"}
|
%{title: "bbb"}
|
||||||
] =
|
] =
|
||||||
Api.read!(
|
Ash.read!(
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:count_of_comments)
|
|> Ash.Query.load(:count_of_comments)
|
||||||
|> Ash.Query.sort(title: :asc, score: :asc)
|
|> Ash.Query.sort(title: :asc, score: :asc)
|
||||||
|
@ -34,31 +34,31 @@ defmodule AshPostgres.SortTest do
|
||||||
test "multi-column sorts work on inclusion" do
|
test "multi-column sorts work on inclusion" do
|
||||||
post =
|
post =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "aaa", likes: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", likes: 1})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "bbb", likes: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", likes: 1})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Comment
|
Comment
|
||||||
|> Ash.Changeset.new(%{title: "aaa", likes: 2})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", likes: 2})
|
||||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
posts =
|
posts =
|
||||||
Post
|
Post
|
||||||
|
@ -71,7 +71,7 @@ defmodule AshPostgres.SortTest do
|
||||||
|> Ash.Query.limit(1)
|
|> Ash.Query.limit(1)
|
||||||
])
|
])
|
||||||
|> Ash.Query.sort([:title, :score])
|
|> Ash.Query.sort([:title, :score])
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "aaa", comments: [%{title: "aaa"}]},
|
%{title: "aaa", comments: [%{title: "aaa"}]},
|
||||||
|
@ -82,23 +82,23 @@ defmodule AshPostgres.SortTest do
|
||||||
|
|
||||||
test "multicolumn sort works with a select statement" do
|
test "multicolumn sort works with a select statement" do
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "aaa", score: 0},
|
%{title: "aaa", score: 0},
|
||||||
%{title: "aaa", score: 1},
|
%{title: "aaa", score: 1},
|
||||||
%{title: "bbb"}
|
%{title: "bbb"}
|
||||||
] =
|
] =
|
||||||
Api.read!(
|
Ash.read!(
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(title: :asc, score: :asc)
|
|> Ash.Query.sort(title: :asc, score: :asc)
|
||||||
|> Ash.Query.select([:title, :score])
|
|> Ash.Query.select([:title, :score])
|
||||||
|
@ -108,43 +108,43 @@ defmodule AshPostgres.SortTest do
|
||||||
test "sorting when joining to a many to many relationship sorts properly" do
|
test "sorting when joining to a many to many relationship sorts properly" do
|
||||||
post1 =
|
post1 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2 =
|
post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "bbb", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post3 =
|
post3 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "ccc", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "ccc", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
PostLink
|
PostLink
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:source_post, post1, type: :append)
|
|> Ash.Changeset.manage_relationship(:source_post, post1, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append)
|
|> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
PostLink
|
PostLink
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:source_post, post2, type: :append)
|
|> Ash.Changeset.manage_relationship(:source_post, post2, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append)
|
|> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
PostLink
|
PostLink
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:source_post, post3, type: :append)
|
|> Ash.Changeset.manage_relationship(:source_post, post3, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append)
|
|> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{title: "aaa"},
|
%{title: "aaa"},
|
||||||
%{title: "bbb"},
|
%{title: "bbb"},
|
||||||
%{title: "ccc"}
|
%{title: "ccc"}
|
||||||
] =
|
] =
|
||||||
Api.read!(
|
Ash.read!(
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(title: :asc)
|
|> Ash.Query.sort(title: :asc)
|
||||||
|> Ash.Query.filter(linked_posts.title in ["aaa", "bbb", "ccc"])
|
|> Ash.Query.filter(linked_posts.title in ["aaa", "bbb", "ccc"])
|
||||||
|
@ -155,7 +155,7 @@ defmodule AshPostgres.SortTest do
|
||||||
%{title: "bbb"},
|
%{title: "bbb"},
|
||||||
%{title: "aaa"}
|
%{title: "aaa"}
|
||||||
] =
|
] =
|
||||||
Api.read!(
|
Ash.read!(
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(title: :desc)
|
|> Ash.Query.sort(title: :desc)
|
||||||
|> Ash.Query.filter(linked_posts.title in ["aaa", "bbb", "ccc"] or title == "aaa")
|
|> Ash.Query.filter(linked_posts.title in ["aaa", "bbb", "ccc"] or title == "aaa")
|
||||||
|
@ -166,7 +166,7 @@ defmodule AshPostgres.SortTest do
|
||||||
%{title: "bbb"},
|
%{title: "bbb"},
|
||||||
%{title: "aaa"}
|
%{title: "aaa"}
|
||||||
] =
|
] =
|
||||||
Api.read!(
|
Ash.read!(
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.sort(title: :desc)
|
|> Ash.Query.sort(title: :desc)
|
||||||
|> Ash.Query.filter(
|
|> Ash.Query.filter(
|
||||||
|
@ -180,48 +180,48 @@ defmodule AshPostgres.SortTest do
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(:count_of_comments)
|
|> Ash.Query.load(:count_of_comments)
|
||||||
|> Ash.Query.sort(:c_times_p)
|
|> Ash.Query.sort(:c_times_p)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calculations can sort on expressions" do
|
test "calculations can sort on expressions" do
|
||||||
post1 =
|
post1 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post2 =
|
post2 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "bbb", score: 1})
|
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 1})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
post3 =
|
post3 =
|
||||||
Post
|
Post
|
||||||
|> Ash.Changeset.new(%{title: "ccc", score: 0})
|
|> Ash.Changeset.for_create(:create, %{title: "ccc", score: 0})
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
PostLink
|
PostLink
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:source_post, post1, type: :append)
|
|> Ash.Changeset.manage_relationship(:source_post, post1, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append)
|
|> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
PostLink
|
PostLink
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:source_post, post2, type: :append)
|
|> Ash.Changeset.manage_relationship(:source_post, post2, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append)
|
|> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
PostLink
|
PostLink
|
||||||
|> Ash.Changeset.new()
|
|> Ash.Changeset.new()
|
||||||
|> Ash.Changeset.manage_relationship(:source_post, post3, type: :append)
|
|> Ash.Changeset.manage_relationship(:source_post, post3, type: :append)
|
||||||
|> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append)
|
|> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append)
|
||||||
|> Api.create!()
|
|> Ash.create!()
|
||||||
|
|
||||||
posts_query =
|
posts_query =
|
||||||
Ash.Query.sort(Post, Ash.Sort.expr_sort(source(post_links.state)))
|
Ash.Query.sort(Post, Ash.Sort.expr_sort(source(post_links.state)))
|
||||||
|
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.load(linked_posts: posts_query)
|
|> Ash.Query.load(linked_posts: posts_query)
|
||||||
|> Api.read!()
|
|> Ash.read!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
defmodule AshPostgres.Test.Api do
|
|
||||||
@moduledoc false
|
|
||||||
use Ash.Api
|
|
||||||
|
|
||||||
resources do
|
|
||||||
registry(AshPostgres.Test.Registry)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,8 +0,0 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.Api do
|
|
||||||
@moduledoc false
|
|
||||||
use Ash.Api
|
|
||||||
|
|
||||||
resources do
|
|
||||||
registry(AshPostgres.Test.ComplexCalculations.Registry)
|
|
||||||
end
|
|
||||||
end
|
|
17
test/support/complex_calculations/domain.ex
Normal file
17
test/support/complex_calculations/domain.ex
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
defmodule AshPostgres.Test.ComplexCalculations.Domain do
|
||||||
|
@moduledoc false
|
||||||
|
use Ash.Domain
|
||||||
|
|
||||||
|
resources do
|
||||||
|
resource(AshPostgres.Test.ComplexCalculations.Certification)
|
||||||
|
resource(AshPostgres.Test.ComplexCalculations.Skill)
|
||||||
|
resource(AshPostgres.Test.ComplexCalculations.Documentation)
|
||||||
|
resource(AshPostgres.Test.ComplexCalculations.Channel)
|
||||||
|
resource(AshPostgres.Test.ComplexCalculations.DMChannel)
|
||||||
|
resource(AshPostgres.Test.ComplexCalculations.ChannelMember)
|
||||||
|
end
|
||||||
|
|
||||||
|
authorization do
|
||||||
|
authorize(:when_requested)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,13 +0,0 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.Registry do
|
|
||||||
@moduledoc false
|
|
||||||
use Ash.Registry
|
|
||||||
|
|
||||||
entries do
|
|
||||||
entry(AshPostgres.Test.ComplexCalculations.Certification)
|
|
||||||
entry(AshPostgres.Test.ComplexCalculations.Skill)
|
|
||||||
entry(AshPostgres.Test.ComplexCalculations.Documentation)
|
|
||||||
entry(AshPostgres.Test.ComplexCalculations.Channel)
|
|
||||||
entry(AshPostgres.Test.ComplexCalculations.DMChannel)
|
|
||||||
entry(AshPostgres.Test.ComplexCalculations.ChannelMember)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,8 +1,11 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.Certification do
|
defmodule AshPostgres.Test.ComplexCalculations.Certification do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||||
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,6 +46,8 @@ defmodule AshPostgres.Test.ComplexCalculations.Certification do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_many(:skills, AshPostgres.Test.ComplexCalculations.Skill)
|
has_many(:skills, AshPostgres.Test.ComplexCalculations.Skill) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.Channel do
|
defmodule AshPostgres.Test.ComplexCalculations.Channel do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [Ash.Policy.Authorizer]
|
authorizers: [Ash.Policy.Authorizer]
|
||||||
|
|
||||||
require Ash.Expr
|
require Ash.Expr
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
|
|
||||||
create_timestamp(:created_at, private?: false)
|
create_timestamp(:created_at, public?: true)
|
||||||
update_timestamp(:updated_at, private?: false)
|
update_timestamp(:updated_at, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -23,30 +25,36 @@ defmodule AshPostgres.Test.ComplexCalculations.Channel do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_many(:channel_members, AshPostgres.Test.ComplexCalculations.ChannelMember)
|
has_many(:channel_members, AshPostgres.Test.ComplexCalculations.ChannelMember) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
has_one :first_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
has_one :first_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
|
public?(true)
|
||||||
destination_attribute(:channel_id)
|
destination_attribute(:channel_id)
|
||||||
from_many?(true)
|
from_many?(true)
|
||||||
sort(created_at: :asc)
|
sort(created_at: :asc)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_one :second_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
has_one :second_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
|
public?(true)
|
||||||
destination_attribute(:channel_id)
|
destination_attribute(:channel_id)
|
||||||
from_many?(true)
|
from_many?(true)
|
||||||
sort(created_at: :desc)
|
sort(created_at: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_one :dm_channel, AshPostgres.Test.ComplexCalculations.DMChannel do
|
has_one :dm_channel, AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||||
api(AshPostgres.Test.ComplexCalculations.Api)
|
public?(true)
|
||||||
|
domain(AshPostgres.Test.ComplexCalculations.Domain)
|
||||||
destination_attribute(:id)
|
destination_attribute(:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_one :dm_channel_with_same_id, AshPostgres.Test.ComplexCalculations.DMChannel do
|
has_one :dm_channel_with_same_id, AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||||
|
public?(true)
|
||||||
no_attributes?(true)
|
no_attributes?(true)
|
||||||
from_many?(true)
|
from_many?(true)
|
||||||
filter(expr(parent(id) == id))
|
filter(expr(parent(id) == id))
|
||||||
api(AshPostgres.Test.ComplexCalculations.Api)
|
domain(AshPostgres.Test.ComplexCalculations.Domain)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.ChannelMember do
|
defmodule AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [Ash.Policy.Authorizer]
|
authorizers: [Ash.Policy.Authorizer]
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
|
|
||||||
create_timestamp(:created_at, private?: false)
|
create_timestamp(:created_at, public?: true)
|
||||||
update_timestamp(:updated_at, private?: false)
|
update_timestamp(:updated_at, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -21,8 +24,8 @@ defmodule AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:user, AshPostgres.Test.User, api: AshPostgres.Test.Api, attribute_writable?: true)
|
belongs_to(:user, AshPostgres.Test.User, domain: AshPostgres.Test.Domain, public?: true)
|
||||||
|
|
||||||
belongs_to(:channel, AshPostgres.Test.ComplexCalculations.Channel, attribute_writable?: true)
|
belongs_to(:channel, AshPostgres.Test.ComplexCalculations.Channel, public?: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.DMChannel do
|
defmodule AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [Ash.Policy.Authorizer]
|
authorizers: [Ash.Policy.Authorizer]
|
||||||
|
|
||||||
require Ash.Expr
|
require Ash.Expr
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
|
|
||||||
create_timestamp(:created_at, private?: false)
|
create_timestamp(:created_at, public?: true)
|
||||||
update_timestamp(:updated_at, private?: false)
|
update_timestamp(:updated_at, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -24,16 +27,19 @@ defmodule AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_many :channel_members, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
has_many :channel_members, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
|
public?(true)
|
||||||
destination_attribute(:channel_id)
|
destination_attribute(:channel_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_one :first_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
has_one :first_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
|
public?(true)
|
||||||
destination_attribute(:channel_id)
|
destination_attribute(:channel_id)
|
||||||
from_many?(true)
|
from_many?(true)
|
||||||
sort(created_at: :asc)
|
sort(created_at: :asc)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_one :second_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
has_one :second_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||||
|
public?(true)
|
||||||
destination_attribute(:channel_id)
|
destination_attribute(:channel_id)
|
||||||
from_many?(true)
|
from_many?(true)
|
||||||
sort(created_at: :desc)
|
sort(created_at: :desc)
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||||
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -15,12 +19,13 @@ defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
||||||
constraints: [
|
constraints: [
|
||||||
one_of: [:demonstrated, :performed, :approved, :reopened]
|
one_of: [:demonstrated, :performed, :approved, :reopened]
|
||||||
],
|
],
|
||||||
|
public?: true,
|
||||||
allow_nil?: false
|
allow_nil?: false
|
||||||
)
|
)
|
||||||
|
|
||||||
attribute(:documented_at, :utc_datetime_usec)
|
attribute(:documented_at, :utc_datetime_usec, public?: true)
|
||||||
create_timestamp(:inserted_at, private?: false)
|
create_timestamp(:inserted_at, public?: true, writable?: true)
|
||||||
update_timestamp(:updated_at, private?: false)
|
update_timestamp(:updated_at, public?: true, writable?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
calculations do
|
calculations do
|
||||||
|
@ -43,6 +48,8 @@ defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:skill, AshPostgres.Test.ComplexCalculations.Skill)
|
belongs_to(:skill, AshPostgres.Test.ComplexCalculations.Skill) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||||
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,7 +18,7 @@ defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:removed, :boolean, default: false, allow_nil?: false)
|
attribute(:removed, :boolean, default: false, allow_nil?: false, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
calculations do
|
calculations do
|
||||||
|
@ -37,13 +41,17 @@ defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:certification, AshPostgres.Test.ComplexCalculations.Certification)
|
belongs_to(:certification, AshPostgres.Test.ComplexCalculations.Certification) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
has_many :documentations, AshPostgres.Test.ComplexCalculations.Documentation do
|
has_many :documentations, AshPostgres.Test.ComplexCalculations.Documentation do
|
||||||
|
public?(true)
|
||||||
sort(timestamp: :desc, inserted_at: :desc)
|
sort(timestamp: :desc, inserted_at: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_one :latest_documentation, AshPostgres.Test.ComplexCalculations.Documentation do
|
has_one :latest_documentation, AshPostgres.Test.ComplexCalculations.Documentation do
|
||||||
|
public?(true)
|
||||||
sort(timestamp: :desc, inserted_at: :desc)
|
sort(timestamp: :desc, inserted_at: :desc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule AshPostgres.Test.Concat do
|
defmodule AshPostgres.Test.Concat do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Calculation
|
use Ash.Resource.Calculation
|
||||||
require Ash.Query
|
require Ash.Query
|
||||||
|
|
||||||
def init(opts) do
|
def init(opts) do
|
||||||
|
@ -11,16 +11,16 @@ defmodule AshPostgres.Test.Concat do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def expression(opts, %{separator: separator}) do
|
def expression(opts, %{arguments: %{separator: separator}}) do
|
||||||
Enum.reduce(opts[:keys], nil, fn key, expr ->
|
Enum.reduce(opts[:keys], nil, fn key, expr ->
|
||||||
if expr do
|
if expr do
|
||||||
if separator do
|
if separator do
|
||||||
Ash.Query.expr(^expr <> ^separator <> ref(^key))
|
expr(^expr <> ^separator <> ^ref(key))
|
||||||
else
|
else
|
||||||
Ash.Query.expr(^expr <> ref(^key))
|
expr(^expr <> ^ref(key))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
Ash.Query.expr(ref(^key))
|
expr(^ref(key))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
27
test/support/domain.ex
Normal file
27
test/support/domain.ex
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
defmodule AshPostgres.Test.Domain do
|
||||||
|
@moduledoc false
|
||||||
|
use Ash.Domain
|
||||||
|
|
||||||
|
resources do
|
||||||
|
resource(AshPostgres.Test.Post)
|
||||||
|
resource(AshPostgres.Test.Comment)
|
||||||
|
resource(AshPostgres.Test.IntegerPost)
|
||||||
|
resource(AshPostgres.Test.Rating)
|
||||||
|
resource(AshPostgres.Test.PostLink)
|
||||||
|
resource(AshPostgres.Test.PostView)
|
||||||
|
resource(AshPostgres.Test.Author)
|
||||||
|
resource(AshPostgres.Test.Profile)
|
||||||
|
resource(AshPostgres.Test.User)
|
||||||
|
resource(AshPostgres.Test.Account)
|
||||||
|
resource(AshPostgres.Test.Organization)
|
||||||
|
resource(AshPostgres.Test.Manager)
|
||||||
|
resource(AshPostgres.Test.Entity)
|
||||||
|
resource(AshPostgres.Test.TempEntity)
|
||||||
|
resource(AshPostgres.Test.Record)
|
||||||
|
resource(AshPostgres.Test.PostFollower)
|
||||||
|
end
|
||||||
|
|
||||||
|
authorization do
|
||||||
|
authorize(:when_requested)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +0,0 @@
|
||||||
defmodule AshPostgres.MultitenancyTest.Api do
|
|
||||||
@moduledoc false
|
|
||||||
use Ash.Api
|
|
||||||
|
|
||||||
resources do
|
|
||||||
registry(AshPostgres.MultitenancyTest.Registry)
|
|
||||||
end
|
|
||||||
end
|
|
10
test/support/multitenancy/domain.ex
Normal file
10
test/support/multitenancy/domain.ex
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule AshPostgres.MultitenancyTest.Domain do
|
||||||
|
@moduledoc false
|
||||||
|
use Ash.Domain
|
||||||
|
|
||||||
|
resources do
|
||||||
|
resource(AshPostgres.MultitenancyTest.Org)
|
||||||
|
resource(AshPostgres.MultitenancyTest.User)
|
||||||
|
resource(AshPostgres.MultitenancyTest.Post)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,10 +0,0 @@
|
||||||
defmodule AshPostgres.MultitenancyTest.Registry do
|
|
||||||
@moduledoc false
|
|
||||||
use Ash.Registry
|
|
||||||
|
|
||||||
entries do
|
|
||||||
entry(AshPostgres.MultitenancyTest.Org)
|
|
||||||
entry(AshPostgres.MultitenancyTest.User)
|
|
||||||
entry(AshPostgres.MultitenancyTest.Post)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.MultitenancyTest.Org do
|
defmodule AshPostgres.MultitenancyTest.Org do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.MultitenancyTest.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
identities do
|
identities do
|
||||||
|
@ -9,10 +10,12 @@ defmodule AshPostgres.MultitenancyTest.Org do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,8 +36,15 @@ defmodule AshPostgres.MultitenancyTest.Org do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_many(:posts, AshPostgres.MultitenancyTest.Post, destination_attribute: :org_id)
|
has_many(:posts, AshPostgres.MultitenancyTest.Post,
|
||||||
has_many(:users, AshPostgres.MultitenancyTest.User, destination_attribute: :org_id)
|
destination_attribute: :org_id,
|
||||||
|
public?: true
|
||||||
|
)
|
||||||
|
|
||||||
|
has_many(:users, AshPostgres.MultitenancyTest.User,
|
||||||
|
destination_attribute: :org_id,
|
||||||
|
public?: true
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tenant("org_" <> tenant) do
|
def tenant("org_" <> tenant) do
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.MultitenancyTest.Post do
|
defmodule AshPostgres.MultitenancyTest.Post do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.MultitenancyTest.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [Ash.Policy.Authorizer]
|
authorizers: [Ash.Policy.Authorizer]
|
||||||
|
|
||||||
|
@ -17,10 +18,12 @@ defmodule AshPostgres.MultitenancyTest.Post do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
|
|
||||||
update(:update_with_policy)
|
update(:update_with_policy)
|
||||||
|
@ -38,8 +41,14 @@ defmodule AshPostgres.MultitenancyTest.Post do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:org, AshPostgres.MultitenancyTest.Org)
|
belongs_to(:org, AshPostgres.MultitenancyTest.Org) do
|
||||||
belongs_to(:user, AshPostgres.MultitenancyTest.User)
|
public?(true)
|
||||||
has_one(:self, __MODULE__, destination_attribute: :id, source_attribute: :id)
|
end
|
||||||
|
|
||||||
|
belongs_to(:user, AshPostgres.MultitenancyTest.User) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
has_one(:self, __MODULE__, destination_attribute: :id, source_attribute: :id, public?: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
defmodule AshPostgres.MultitenancyTest.User do
|
defmodule AshPostgres.MultitenancyTest.User do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.MultitenancyTest.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
attribute(:org_id, :uuid)
|
attribute(:org_id, :uuid, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -15,6 +16,8 @@ defmodule AshPostgres.MultitenancyTest.User do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,7 +31,9 @@ defmodule AshPostgres.MultitenancyTest.User do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:org, AshPostgres.MultitenancyTest.Org)
|
belongs_to(:org, AshPostgres.MultitenancyTest.Org) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_tenant("org_" <> id), do: id
|
def parse_tenant("org_" <> id), do: id
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
defmodule AshPostgres.Test.Registry do
|
|
||||||
@moduledoc false
|
|
||||||
use Ash.Registry
|
|
||||||
|
|
||||||
entries do
|
|
||||||
entry(AshPostgres.Test.Post)
|
|
||||||
entry(AshPostgres.Test.Comment)
|
|
||||||
entry(AshPostgres.Test.IntegerPost)
|
|
||||||
entry(AshPostgres.Test.Rating)
|
|
||||||
entry(AshPostgres.Test.PostLink)
|
|
||||||
entry(AshPostgres.Test.PostView)
|
|
||||||
entry(AshPostgres.Test.Author)
|
|
||||||
entry(AshPostgres.Test.Profile)
|
|
||||||
entry(AshPostgres.Test.User)
|
|
||||||
entry(AshPostgres.Test.Account)
|
|
||||||
entry(AshPostgres.Test.Organization)
|
|
||||||
entry(AshPostgres.Test.Manager)
|
|
||||||
entry(AshPostgres.Test.Entity)
|
|
||||||
entry(AshPostgres.Test.TempEntity)
|
|
||||||
entry(AshPostgres.Test.Record)
|
|
||||||
entry(AshPostgres.Test.PostFollower)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -13,7 +13,7 @@ defmodule AshPostgres.Test.Post.CommentsContainingTitle do
|
||||||
query
|
query
|
||||||
|> Ash.Query.filter(post_id in ^post_ids)
|
|> Ash.Query.filter(post_id in ^post_ids)
|
||||||
|> Ash.Query.filter(contains(title, post.title))
|
|> Ash.Query.filter(contains(title, post.title))
|
||||||
|> AshPostgres.Test.Api.read!(actor: actor, authorize?: authorize?)
|
|> Ash.read!(actor: actor, authorize?: authorize?)
|
||||||
|> Enum.group_by(& &1.post_id)}
|
|> Enum.group_by(& &1.post_id)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
defmodule AshPostgres.Test.Account do
|
defmodule AshPostgres.Test.Account do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -12,7 +16,7 @@ defmodule AshPostgres.Test.Account do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:is_active, :boolean)
|
attribute(:is_active, :boolean, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
calculations do
|
calculations do
|
||||||
|
@ -30,6 +34,8 @@ defmodule AshPostgres.Test.Account do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:user, AshPostgres.Test.User)
|
belongs_to(:user, AshPostgres.Test.User) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
defmodule AshPostgres.Test.Author do
|
defmodule AshPostgres.Test.Author do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
defmodule RuntimeFullName do
|
defmodule RuntimeFullName do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Calculation
|
use Ash.Resource.Calculation
|
||||||
|
|
||||||
def calculate(records, _, _) do
|
def calculate(records, _, _) do
|
||||||
Enum.map(records, fn record ->
|
Enum.map(records, fn record ->
|
||||||
|
@ -21,21 +22,29 @@ defmodule AshPostgres.Test.Author do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:first_name, :string)
|
attribute(:first_name, :string, public?: true)
|
||||||
attribute(:last_name, :string)
|
attribute(:last_name, :string, public?: true)
|
||||||
attribute(:bio, AshPostgres.Test.Bio)
|
attribute(:bio, AshPostgres.Test.Bio, public?: true)
|
||||||
attribute(:badges, {:array, :atom})
|
attribute(:badges, {:array, :atom}, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_one(:profile, AshPostgres.Test.Profile)
|
has_one(:profile, AshPostgres.Test.Profile) do
|
||||||
has_many(:posts, AshPostgres.Test.Post)
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
has_many(:posts, AshPostgres.Test.Post) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
has_many :authors_with_same_first_name, __MODULE__ do
|
has_many :authors_with_same_first_name, __MODULE__ do
|
||||||
|
public?(true)
|
||||||
source_attribute(:first_name)
|
source_attribute(:first_name)
|
||||||
destination_attribute(:first_name)
|
destination_attribute(:first_name)
|
||||||
filter(expr(parent(id) != id))
|
filter(expr(parent(id) != id))
|
||||||
|
|
|
@ -3,15 +3,18 @@ defmodule AshPostgres.Test.Bio do
|
||||||
use Ash.Resource, data_layer: :embedded
|
use Ash.Resource, data_layer: :embedded
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
attribute(:title, :string)
|
attribute(:title, :string, public?: true)
|
||||||
attribute(:bio, :string)
|
attribute(:bio, :string, public?: true)
|
||||||
attribute(:years_of_experience, :integer)
|
attribute(:years_of_experience, :integer, public?: true)
|
||||||
|
|
||||||
attribute :list_of_strings, {:array, :string} do
|
attribute :list_of_strings, {:array, :string} do
|
||||||
|
public?(true)
|
||||||
allow_nil?(true)
|
allow_nil?(true)
|
||||||
default(nil)
|
default(nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.Comment do
|
defmodule AshPostgres.Test.Comment do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [
|
authorizers: [
|
||||||
Ash.Policy.Authorizer
|
Ash.Policy.Authorizer
|
||||||
|
@ -23,6 +24,7 @@ defmodule AshPostgres.Test.Comment do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
defaults([:read, :update, :destroy])
|
defaults([:read, :update, :destroy])
|
||||||
|
|
||||||
create :create do
|
create :create do
|
||||||
|
@ -35,10 +37,10 @@ defmodule AshPostgres.Test.Comment do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:title, :string)
|
attribute(:title, :string, public?: true)
|
||||||
attribute(:likes, :integer)
|
attribute(:likes, :integer, public?: true)
|
||||||
attribute(:arbitrary_timestamp, :utc_datetime_usec)
|
attribute(:arbitrary_timestamp, :utc_datetime_usec, public?: true)
|
||||||
create_timestamp(:created_at, writable?: true)
|
create_timestamp(:created_at, writable?: true, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
aggregates do
|
aggregates do
|
||||||
|
@ -49,15 +51,22 @@ defmodule AshPostgres.Test.Comment do
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:post, AshPostgres.Test.Post)
|
belongs_to(:post, AshPostgres.Test.Post) do
|
||||||
belongs_to(:author, AshPostgres.Test.Author)
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
belongs_to(:author, AshPostgres.Test.Author) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
has_many(:ratings, AshPostgres.Test.Rating,
|
has_many(:ratings, AshPostgres.Test.Rating,
|
||||||
|
public?: true,
|
||||||
destination_attribute: :resource_id,
|
destination_attribute: :resource_id,
|
||||||
relationship_context: %{data_layer: %{table: "comment_ratings"}}
|
relationship_context: %{data_layer: %{table: "comment_ratings"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
has_many(:popular_ratings, AshPostgres.Test.Rating,
|
has_many(:popular_ratings, AshPostgres.Test.Rating,
|
||||||
|
public?: true,
|
||||||
destination_attribute: :resource_id,
|
destination_attribute: :resource_id,
|
||||||
relationship_context: %{data_layer: %{table: "comment_ratings"}},
|
relationship_context: %{data_layer: %{table: "comment_ratings"}},
|
||||||
filter: expr(score > 5)
|
filter: expr(score > 5)
|
||||||
|
|
|
@ -2,14 +2,15 @@ defmodule AshPostgres.Test.Entity do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
|
|
||||||
attribute(:full_name, :string, allow_nil?: false)
|
attribute(:full_name, :string, allow_nil?: false, public?: true)
|
||||||
|
|
||||||
timestamps(private?: false)
|
timestamps(public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -18,6 +19,8 @@ defmodule AshPostgres.Test.Entity do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read])
|
defaults([:create, :read])
|
||||||
|
|
||||||
read :read_from_temp do
|
read :read_from_temp do
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.IntegerPost do
|
defmodule AshPostgres.Test.IntegerPost do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -9,11 +10,13 @@ defmodule AshPostgres.Test.IntegerPost do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
integer_primary_key(:id)
|
integer_primary_key(:id)
|
||||||
attribute(:title, :string)
|
attribute(:title, :string, public?: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.Manager do
|
defmodule AshPostgres.Test.Manager do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -9,6 +10,8 @@ defmodule AshPostgres.Test.Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:read, :update, :destroy])
|
defaults([:read, :update, :destroy])
|
||||||
|
|
||||||
create :create do
|
create :create do
|
||||||
|
@ -25,14 +28,15 @@ defmodule AshPostgres.Test.Manager do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
attribute(:code, :string, allow_nil?: false)
|
attribute(:code, :string, allow_nil?: false, public?: true)
|
||||||
attribute(:must_be_present, :string, allow_nil?: false)
|
attribute(:must_be_present, :string, allow_nil?: false, public?: true)
|
||||||
attribute(:role, :string)
|
attribute(:role, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to :organization, AshPostgres.Test.Organization do
|
belongs_to :organization, AshPostgres.Test.Organization do
|
||||||
|
public?(true)
|
||||||
attribute_writable?(true)
|
attribute_writable?(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.Organization do
|
defmodule AshPostgres.Test.Organization do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [
|
authorizers: [
|
||||||
Ash.Policy.Authorizer
|
Ash.Policy.Authorizer
|
||||||
|
@ -24,17 +25,27 @@ defmodule AshPostgres.Test.Organization do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:name, :string)
|
attribute(:name, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_many(:users, AshPostgres.Test.User)
|
has_many(:users, AshPostgres.Test.User) do
|
||||||
has_many(:posts, AshPostgres.Test.Post)
|
public?(true)
|
||||||
has_many(:managers, AshPostgres.Test.Manager)
|
end
|
||||||
|
|
||||||
|
has_many(:posts, AshPostgres.Test.Post) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
has_many(:managers, AshPostgres.Test.Manager) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ end
|
||||||
defmodule AshPostgres.Test.Post do
|
defmodule AshPostgres.Test.Post do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [
|
authorizers: [
|
||||||
Ash.Policy.Authorizer
|
Ash.Policy.Authorizer
|
||||||
|
@ -73,7 +74,14 @@ defmodule AshPostgres.Test.Post do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
defaults([:update, :destroy])
|
default_accept(:*)
|
||||||
|
|
||||||
|
defaults([:destroy])
|
||||||
|
|
||||||
|
update :update do
|
||||||
|
primary?(true)
|
||||||
|
require_atomic?(false)
|
||||||
|
end
|
||||||
|
|
||||||
read :title_is_foo do
|
read :title_is_foo do
|
||||||
filter(expr(title == "foo"))
|
filter(expr(title == "foo"))
|
||||||
|
@ -113,6 +121,7 @@ defmodule AshPostgres.Test.Post do
|
||||||
end
|
end
|
||||||
|
|
||||||
update :manual_update do
|
update :manual_update do
|
||||||
|
require_atomic?(false)
|
||||||
manual(AshPostgres.Test.Post.ManualUpdate)
|
manual(AshPostgres.Test.Post.ManualUpdate)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -125,83 +134,103 @@ defmodule AshPostgres.Test.Post do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
|
|
||||||
attribute(:title, :string) do
|
attribute(:title, :string) do
|
||||||
|
public?(true)
|
||||||
source(:title_column)
|
source(:title_column)
|
||||||
end
|
end
|
||||||
|
|
||||||
attribute(:score, :integer)
|
attribute(:score, :integer, public?: true)
|
||||||
attribute(:public, :boolean)
|
attribute(:public, :boolean, public?: true)
|
||||||
attribute(:category, :ci_string)
|
attribute(:category, :ci_string, public?: true)
|
||||||
attribute(:type, :atom, default: :sponsored, private?: true, writable?: false)
|
attribute(:type, :atom, default: :sponsored, writable?: false, public?: false)
|
||||||
attribute(:price, :integer)
|
attribute(:price, :integer, public?: true)
|
||||||
attribute(:decimal, :decimal, default: Decimal.new(0))
|
attribute(:decimal, :decimal, default: Decimal.new(0), public?: true)
|
||||||
attribute(:status, AshPostgres.Test.Types.Status)
|
attribute(:status, AshPostgres.Test.Types.Status, public?: true)
|
||||||
attribute(:status_enum, AshPostgres.Test.Types.StatusEnum)
|
attribute(:status_enum, AshPostgres.Test.Types.StatusEnum, public?: true)
|
||||||
attribute(:status_enum_no_cast, AshPostgres.Test.Types.StatusEnumNoCast, source: :status_enum)
|
|
||||||
attribute(:point, AshPostgres.Test.Point)
|
attribute(:status_enum_no_cast, AshPostgres.Test.Types.StatusEnumNoCast,
|
||||||
attribute(:composite_point, AshPostgres.Test.CompositePoint)
|
source: :status_enum,
|
||||||
attribute(:stuff, :map)
|
public?: true
|
||||||
attribute(:list_of_stuff, {:array, :map})
|
)
|
||||||
attribute(:uniq_one, :string)
|
|
||||||
attribute(:uniq_two, :string)
|
attribute(:point, AshPostgres.Test.Point, public?: true)
|
||||||
attribute(:uniq_custom_one, :string)
|
attribute(:composite_point, AshPostgres.Test.CompositePoint, public?: true)
|
||||||
attribute(:uniq_custom_two, :string)
|
attribute(:stuff, :map, public?: true)
|
||||||
|
attribute(:list_of_stuff, {:array, :map}, public?: true)
|
||||||
|
attribute(:uniq_one, :string, public?: true)
|
||||||
|
attribute(:uniq_two, :string, public?: true)
|
||||||
|
attribute(:uniq_custom_one, :string, public?: true)
|
||||||
|
attribute(:uniq_custom_two, :string, public?: true)
|
||||||
|
|
||||||
attribute :list_containing_nils, {:array, :string} do
|
attribute :list_containing_nils, {:array, :string} do
|
||||||
|
public?(true)
|
||||||
constraints(nil_items?: true)
|
constraints(nil_items?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
create_timestamp(:created_at)
|
create_timestamp(:created_at, writable?: true, public?: true)
|
||||||
update_timestamp(:updated_at)
|
update_timestamp(:updated_at, writable?: true, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
code_interface do
|
code_interface do
|
||||||
define_for(AshPostgres.Test.Api)
|
define(:create, args: [:title])
|
||||||
define(:get_by_id, action: :read, get_by: [:id])
|
define(:get_by_id, action: :read, get_by: [:id])
|
||||||
define(:increment_score, args: [{:optional, :amount}])
|
define(:increment_score, args: [{:optional, :amount}])
|
||||||
|
define(:destroy)
|
||||||
|
define(:bulk_create, bulk?: true, action: :create)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to :organization, AshPostgres.Test.Organization do
|
belongs_to :organization, AshPostgres.Test.Organization do
|
||||||
|
public?(true)
|
||||||
attribute_writable?(true)
|
attribute_writable?(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
belongs_to(:author, AshPostgres.Test.Author)
|
belongs_to(:author, AshPostgres.Test.Author) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
|
|
||||||
has_many :posts_with_matching_title, __MODULE__ do
|
has_many :posts_with_matching_title, __MODULE__ do
|
||||||
|
public?(true)
|
||||||
no_attributes?(true)
|
no_attributes?(true)
|
||||||
filter(expr(parent(title) == title and parent(id) != id))
|
filter(expr(parent(title) == title and parent(id) != id))
|
||||||
end
|
end
|
||||||
|
|
||||||
has_many(:comments, AshPostgres.Test.Comment, destination_attribute: :post_id)
|
has_many(:comments, AshPostgres.Test.Comment, destination_attribute: :post_id, public?: true)
|
||||||
|
|
||||||
has_many :comments_matching_post_title, AshPostgres.Test.Comment do
|
has_many :comments_matching_post_title, AshPostgres.Test.Comment do
|
||||||
|
public?(true)
|
||||||
filter(expr(title == parent_expr(title)))
|
filter(expr(title == parent_expr(title)))
|
||||||
end
|
end
|
||||||
|
|
||||||
has_many :popular_comments, AshPostgres.Test.Comment do
|
has_many :popular_comments, AshPostgres.Test.Comment do
|
||||||
|
public?(true)
|
||||||
destination_attribute(:post_id)
|
destination_attribute(:post_id)
|
||||||
filter(expr(likes > 10))
|
filter(expr(likes > 10))
|
||||||
end
|
end
|
||||||
|
|
||||||
has_many :comments_containing_title, AshPostgres.Test.Comment do
|
has_many :comments_containing_title, AshPostgres.Test.Comment do
|
||||||
|
public?(true)
|
||||||
manual(AshPostgres.Test.Post.CommentsContainingTitle)
|
manual(AshPostgres.Test.Post.CommentsContainingTitle)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_many :comments_with_high_rating, AshPostgres.Test.Comment do
|
has_many :comments_with_high_rating, AshPostgres.Test.Comment do
|
||||||
|
public?(true)
|
||||||
filter(expr(ratings.score > 5))
|
filter(expr(ratings.score > 5))
|
||||||
end
|
end
|
||||||
|
|
||||||
has_many(:ratings, AshPostgres.Test.Rating,
|
has_many(:ratings, AshPostgres.Test.Rating,
|
||||||
|
public?: true,
|
||||||
destination_attribute: :resource_id,
|
destination_attribute: :resource_id,
|
||||||
relationship_context: %{data_layer: %{table: "post_ratings"}}
|
relationship_context: %{data_layer: %{table: "post_ratings"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
has_many(:post_links, AshPostgres.Test.PostLink,
|
has_many(:post_links, AshPostgres.Test.PostLink,
|
||||||
|
public?: true,
|
||||||
destination_attribute: :source_post_id,
|
destination_attribute: :source_post_id,
|
||||||
filter: [state: :active]
|
filter: [state: :active]
|
||||||
)
|
)
|
||||||
|
|
||||||
many_to_many(:linked_posts, __MODULE__,
|
many_to_many(:linked_posts, __MODULE__,
|
||||||
|
public?: true,
|
||||||
through: AshPostgres.Test.PostLink,
|
through: AshPostgres.Test.PostLink,
|
||||||
join_relationship: :post_links,
|
join_relationship: :post_links,
|
||||||
source_attribute_on_join_resource: :source_post_id,
|
source_attribute_on_join_resource: :source_post_id,
|
||||||
|
@ -209,13 +238,16 @@ defmodule AshPostgres.Test.Post do
|
||||||
)
|
)
|
||||||
|
|
||||||
many_to_many(:followers, AshPostgres.Test.User,
|
many_to_many(:followers, AshPostgres.Test.User,
|
||||||
|
public?: true,
|
||||||
through: AshPostgres.Test.PostFollower,
|
through: AshPostgres.Test.PostFollower,
|
||||||
source_attribute_on_join_resource: :post_id,
|
source_attribute_on_join_resource: :post_id,
|
||||||
destination_attribute_on_join_resource: :follower_id,
|
destination_attribute_on_join_resource: :follower_id,
|
||||||
read_action: :active
|
read_action: :active
|
||||||
)
|
)
|
||||||
|
|
||||||
has_many(:views, AshPostgres.Test.PostView)
|
has_many(:views, AshPostgres.Test.PostView) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
validations do
|
validations do
|
||||||
|
@ -483,10 +515,10 @@ end
|
||||||
|
|
||||||
defmodule CalculatePostPriceString do
|
defmodule CalculatePostPriceString do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Calculation
|
use Ash.Resource.Calculation
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def select(_, _, _), do: [:price]
|
def load(_, _, _), do: [:price]
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def calculate(records, _, _) do
|
def calculate(records, _, _) do
|
||||||
|
@ -500,7 +532,7 @@ end
|
||||||
|
|
||||||
defmodule CalculatePostPriceStringWithSymbol do
|
defmodule CalculatePostPriceStringWithSymbol do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Calculation
|
use Ash.Resource.Calculation
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def load(_, _, _), do: [:price_string]
|
def load(_, _, _), do: [:price_string]
|
||||||
|
@ -524,7 +556,7 @@ defmodule AshPostgres.Test.Post.ManualUpdate do
|
||||||
|> Ash.Changeset.for_update(:update, changeset.attributes)
|
|> Ash.Changeset.for_update(:update, changeset.attributes)
|
||||||
|> Ash.Changeset.force_change_attribute(:title, "manual")
|
|> Ash.Changeset.force_change_attribute(:title, "manual")
|
||||||
|> Ash.Changeset.load(:comments)
|
|> Ash.Changeset.load(:comments)
|
||||||
|> AshPostgres.Test.Api.update!()
|
|> Ash.update!()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.PostFollower do
|
defmodule AshPostgres.Test.PostFollower do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -9,6 +10,8 @@ defmodule AshPostgres.Test.PostFollower do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,10 +21,12 @@ defmodule AshPostgres.Test.PostFollower do
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to :post, AshPostgres.Test.Post do
|
belongs_to :post, AshPostgres.Test.Post do
|
||||||
|
public?(true)
|
||||||
allow_nil?(false)
|
allow_nil?(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
belongs_to :follower, AshPostgres.Test.User do
|
belongs_to :follower, AshPostgres.Test.User do
|
||||||
|
public?(true)
|
||||||
allow_nil?(false)
|
allow_nil?(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.PostLink do
|
defmodule AshPostgres.Test.PostLink do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -9,6 +10,8 @@ defmodule AshPostgres.Test.PostLink do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,6 +21,7 @@ defmodule AshPostgres.Test.PostLink do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
attribute :state, :atom do
|
attribute :state, :atom do
|
||||||
|
public?(true)
|
||||||
constraints(one_of: [:active, :archived])
|
constraints(one_of: [:active, :archived])
|
||||||
default(:active)
|
default(:active)
|
||||||
end
|
end
|
||||||
|
@ -25,11 +29,13 @@ defmodule AshPostgres.Test.PostLink do
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to :source_post, AshPostgres.Test.Post do
|
belongs_to :source_post, AshPostgres.Test.Post do
|
||||||
|
public?(true)
|
||||||
allow_nil?(false)
|
allow_nil?(false)
|
||||||
primary_key?(true)
|
primary_key?(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
belongs_to :destination_post, AshPostgres.Test.Post do
|
belongs_to :destination_post, AshPostgres.Test.Post do
|
||||||
|
public?(true)
|
||||||
allow_nil?(false)
|
allow_nil?(false)
|
||||||
primary_key?(true)
|
primary_key?(true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
defmodule AshPostgres.Test.PostView do
|
defmodule AshPostgres.Test.PostView do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read])
|
defaults([:create, :read])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
create_timestamp(:time)
|
create_timestamp(:time)
|
||||||
attribute(:browser, :atom, constraints: [one_of: [:firefox, :chrome, :edge]])
|
attribute(:browser, :atom, constraints: [one_of: [:firefox, :chrome, :edge]], public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to :post, AshPostgres.Test.Post do
|
belongs_to :post, AshPostgres.Test.Post do
|
||||||
|
public?(true)
|
||||||
allow_nil?(false)
|
allow_nil?(false)
|
||||||
attribute_writable?(true)
|
attribute_writable?(true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.Profile do
|
defmodule AshPostgres.Test.Profile do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -11,14 +12,18 @@ defmodule AshPostgres.Test.Profile do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id, writable?: true)
|
uuid_primary_key(:id, writable?: true)
|
||||||
attribute(:description, :string)
|
attribute(:description, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:author, AshPostgres.Test.Author)
|
belongs_to(:author, AshPostgres.Test.Author) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.Rating do
|
defmodule AshPostgres.Test.Rating do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
postgres do
|
postgres do
|
||||||
|
@ -9,12 +10,14 @@ defmodule AshPostgres.Test.Rating do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:score, :integer)
|
attribute(:score, :integer, public?: true)
|
||||||
attribute(:resource_id, :uuid)
|
attribute(:resource_id, :uuid, public?: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,20 +2,22 @@ defmodule AshPostgres.Test.Record do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Domain,
|
||||||
data_layer: AshPostgres.DataLayer
|
data_layer: AshPostgres.DataLayer
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
|
|
||||||
attribute(:full_name, :string, allow_nil?: false)
|
attribute(:full_name, :string, allow_nil?: false, public?: true)
|
||||||
|
|
||||||
timestamps(private?: false)
|
timestamps(public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
alias AshPostgres.Test.Entity
|
alias AshPostgres.Test.Entity
|
||||||
|
|
||||||
has_one :entity, Entity do
|
has_one :entity, Entity do
|
||||||
|
public?(true)
|
||||||
no_attributes?(true)
|
no_attributes?(true)
|
||||||
|
|
||||||
read_action(:read_from_temp)
|
read_action(:read_from_temp)
|
||||||
|
@ -30,6 +32,8 @@ defmodule AshPostgres.Test.Record do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read])
|
defaults([:create, :read])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule AshPostgres.Test.Subquery.Access do
|
||||||
alias AshPostgres.Test.Subquery.Parent
|
alias AshPostgres.Test.Subquery.Parent
|
||||||
|
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Subquery.ParentDomain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [
|
authorizers: [
|
||||||
Ash.Policy.Authorizer
|
Ash.Policy.Authorizer
|
||||||
|
@ -17,19 +18,19 @@ defmodule AshPostgres.Test.Subquery.Access do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:parent_id, :uuid)
|
attribute(:parent_id, :uuid, public?: true)
|
||||||
attribute(:email, :string)
|
attribute(:email, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
code_interface do
|
code_interface do
|
||||||
define_for(AshPostgres.Test.Subquery.ParentApi)
|
|
||||||
|
|
||||||
define(:create)
|
define(:create)
|
||||||
define(:read)
|
define(:read)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
belongs_to(:parent, Parent)
|
belongs_to(:parent, Parent) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
policies do
|
policies do
|
||||||
|
@ -39,6 +40,8 @@ defmodule AshPostgres.Test.Subquery.Access do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :update, :destroy])
|
defaults([:create, :update, :destroy])
|
||||||
|
|
||||||
read :read do
|
read :read do
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule AshPostgres.Test.Subquery.Child do
|
||||||
alias AshPostgres.Test.Subquery.Through
|
alias AshPostgres.Test.Subquery.Through
|
||||||
|
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Subquery.ChildDomain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [
|
authorizers: [
|
||||||
Ash.Policy.Authorizer
|
Ash.Policy.Authorizer
|
||||||
|
@ -15,18 +16,17 @@ defmodule AshPostgres.Test.Subquery.Child do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:state, :string)
|
attribute(:state, :string, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
code_interface do
|
code_interface do
|
||||||
define_for(AshPostgres.Test.Subquery.ChildApi)
|
|
||||||
|
|
||||||
define(:create)
|
define(:create)
|
||||||
define(:read)
|
define(:read)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
has_many :throughs, Through do
|
has_many :throughs, Through do
|
||||||
|
public?(true)
|
||||||
source_attribute(:id)
|
source_attribute(:id)
|
||||||
destination_attribute(:child_id)
|
destination_attribute(:child_id)
|
||||||
end
|
end
|
||||||
|
@ -48,6 +48,8 @@ defmodule AshPostgres.Test.Subquery.Child do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
defmodule AshPostgres.Test.Subquery.ChildApi do
|
defmodule AshPostgres.Test.Subquery.ChildDomain do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
alias AshPostgres.Test.Subquery.Child
|
alias AshPostgres.Test.Subquery.Child
|
||||||
alias AshPostgres.Test.Subquery.Through
|
alias AshPostgres.Test.Subquery.Through
|
||||||
use Ash.Api
|
use Ash.Domain
|
||||||
|
|
||||||
resources do
|
resources do
|
||||||
resource(Child)
|
resource(Child)
|
||||||
resource(Through)
|
resource(Through)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
authorization do
|
||||||
|
authorize(:when_requested)
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule AshPostgres.Test.Subquery.Parent do
|
defmodule AshPostgres.Test.Subquery.Parent do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Ash.Resource,
|
use Ash.Resource,
|
||||||
|
domain: AshPostgres.Test.Subquery.ParentDomain,
|
||||||
data_layer: AshPostgres.DataLayer,
|
data_layer: AshPostgres.DataLayer,
|
||||||
authorizers: [
|
authorizers: [
|
||||||
Ash.Policy.Authorizer
|
Ash.Policy.Authorizer
|
||||||
|
@ -15,22 +16,25 @@ defmodule AshPostgres.Test.Subquery.Parent do
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key(:id)
|
uuid_primary_key(:id)
|
||||||
attribute(:owner_email, :string)
|
attribute(:owner_email, :string, public?: true)
|
||||||
attribute(:other_owner_email, :string)
|
attribute(:other_owner_email, :string, public?: true)
|
||||||
attribute(:visible, :boolean)
|
attribute(:visible, :boolean, public?: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
many_to_many :children, Child do
|
many_to_many :children, Child do
|
||||||
|
public?(true)
|
||||||
through(Through)
|
through(Through)
|
||||||
source_attribute(:id)
|
source_attribute(:id)
|
||||||
source_attribute_on_join_resource(:parent_id)
|
source_attribute_on_join_resource(:parent_id)
|
||||||
destination_attribute(:id)
|
destination_attribute(:id)
|
||||||
destination_attribute_on_join_resource(:child_id)
|
destination_attribute_on_join_resource(:child_id)
|
||||||
api(AshPostgres.Test.Subquery.ChildApi)
|
domain(AshPostgres.Test.Subquery.ChildDomain)
|
||||||
end
|
end
|
||||||
|
|
||||||
has_many(:accesses, Access)
|
has_many(:accesses, Access) do
|
||||||
|
public?(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
policies do
|
policies do
|
||||||
|
@ -48,8 +52,6 @@ defmodule AshPostgres.Test.Subquery.Parent do
|
||||||
end
|
end
|
||||||
|
|
||||||
code_interface do
|
code_interface do
|
||||||
define_for(AshPostgres.Test.Subquery.ParentApi)
|
|
||||||
|
|
||||||
define(:create)
|
define(:create)
|
||||||
define(:read)
|
define(:read)
|
||||||
|
|
||||||
|
@ -57,6 +59,8 @@ defmodule AshPostgres.Test.Subquery.Parent do
|
||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
|
default_accept(:*)
|
||||||
|
|
||||||
defaults([:create, :read, :update, :destroy])
|
defaults([:create, :read, :update, :destroy])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
defmodule AshPostgres.Test.Subquery.ParentApi do
|
defmodule AshPostgres.Test.Subquery.ParentDomain do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
alias AshPostgres.Test.Subquery.Access
|
alias AshPostgres.Test.Subquery.Access
|
||||||
alias AshPostgres.Test.Subquery.Parent
|
alias AshPostgres.Test.Subquery.Parent
|
||||||
use Ash.Api
|
use Ash.Domain
|
||||||
|
|
||||||
resources do
|
resources do
|
||||||
resource(Parent)
|
resource(Parent)
|
||||||
resource(Access)
|
resource(Access)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
authorization do
|
||||||
|
authorize(:when_requested)
|
||||||
|
end
|
||||||
end
|
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue