mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-17 03:53: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
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
title: ""
|
||||
labels: enhancement, needs review
|
||||
assignees: ''
|
||||
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
@ -29,7 +28,7 @@ For example
|
|||
Or
|
||||
|
||||
```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**
|
||||
|
|
2
.github/workflows/elixir.yml
vendored
2
.github/workflows/elixir.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
postgres-version: ["10", "12", "14", "16"]
|
||||
postgres-version: ["14", "15", "16"]
|
||||
uses: ash-project/ash/.github/workflows/ash-ci.yml@main
|
||||
with:
|
||||
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
|
||||
defmodule MyApp.SomeResource do
|
||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
||||
use Ash.Resource, domain: MyDomain, data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
repo MyApp.Repo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.{Domain, Post}
|
||||
|
||||
ten_rows =
|
||||
1..10
|
||||
|
@ -25,7 +25,7 @@ hundred_thousand_rows =
|
|||
end)
|
||||
|
||||
# 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,
|
||||
max_concurrency: 2
|
||||
)
|
||||
|
@ -39,13 +39,13 @@ batch_size = 200
|
|||
Benchee.run(
|
||||
%{
|
||||
"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,
|
||||
transaction: false
|
||||
)
|
||||
end,
|
||||
"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,
|
||||
transaction: false,
|
||||
assume_casted?: true
|
||||
|
@ -62,7 +62,7 @@ Benchee.run(
|
|||
input
|
||||
|> Stream.chunk_every(batch_size)
|
||||
|> 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
|
||||
)
|
||||
end, max_concurrency: max_concurrency, timeout: :infinity)
|
||||
|
@ -72,7 +72,7 @@ Benchee.run(
|
|||
input
|
||||
|> Stream.chunk_every(batch_size)
|
||||
|> 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,
|
||||
assume_casted?: true
|
||||
)
|
||||
|
@ -80,14 +80,14 @@ Benchee.run(
|
|||
|> Stream.run()
|
||||
end,
|
||||
"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,
|
||||
max_concurrency: max_concurrency,
|
||||
batch_size: batch_size
|
||||
)
|
||||
end,
|
||||
"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,
|
||||
assume_casted?: true,
|
||||
max_concurrency: max_concurrency,
|
||||
|
|
|
@ -15,8 +15,8 @@ if Mix.env() == :dev do
|
|||
end
|
||||
|
||||
if Mix.env() == :test do
|
||||
config :ash, :validate_api_resource_inclusion?, false
|
||||
config :ash, :validate_api_config_inclusion?, false
|
||||
config :ash, :validate_domain_resource_inclusion?, false
|
||||
config :ash, :validate_domain_config_inclusion?, false
|
||||
|
||||
config :ash_postgres, AshPostgres.TestRepo,
|
||||
username: "postgres",
|
||||
|
@ -42,10 +42,10 @@ if Mix.env() == :test do
|
|||
|
||||
config :ash_postgres,
|
||||
ecto_repos: [AshPostgres.TestRepo, AshPostgres.TestNoSandboxRepo],
|
||||
ash_apis: [
|
||||
AshPostgres.Test.Api,
|
||||
AshPostgres.MultitenancyTest.Api,
|
||||
AshPostgres.Test.ComplexCalculations.Api
|
||||
ash_domains: [
|
||||
AshPostgres.Test.Domain,
|
||||
AshPostgres.MultitenancyTest.Domain,
|
||||
AshPostgres.Test.ComplexCalculations.Domain
|
||||
]
|
||||
|
||||
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:
|
||||
`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`
|
||||
|
||||
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
|
||||
|
||||
defp repos do
|
||||
apis()
|
||||
|> Enum.flat_map(fn api ->
|
||||
api
|
||||
|> Ash.Api.Info.resources()
|
||||
domains()
|
||||
|> Enum.flat_map(fn domain ->
|
||||
domain
|
||||
|> Ash.Domain.Info.resources()
|
||||
|> Enum.map(&AshPostgres.DataLayer.Info.repo/1)
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
defp apis do
|
||||
Application.fetch_env!(@app, :ash_apis)
|
||||
defp domains do
|
||||
Application.fetch_env!(@app, :ash_domains)
|
||||
end
|
||||
|
||||
defp load_app do
|
||||
|
|
|
@ -5,6 +5,7 @@ To support leveraging the same resource backed by multiple tables (useful for th
|
|||
```elixir
|
||||
defmodule MyApp.Reaction do
|
||||
use Ash.Resource,
|
||||
domain: MyDomain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -12,7 +13,7 @@ defmodule MyApp.Reaction do
|
|||
end
|
||||
|
||||
attributes do
|
||||
attribute(:resource_id, :uuid)
|
||||
attribute :resource_id, :uuid, public?: true
|
||||
end
|
||||
|
||||
...
|
||||
|
@ -24,6 +25,7 @@ Then, in your related resources, you set the table context like so:
|
|||
```elixir
|
||||
defmodule MyApp.Post do
|
||||
use Ash.Resource,
|
||||
domain: MyDomain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
...
|
||||
|
@ -37,6 +39,7 @@ end
|
|||
|
||||
defmodule MyApp.Comment do
|
||||
use Ash.Resource,
|
||||
domain: MyDomain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
...
|
||||
|
@ -61,6 +64,7 @@ For example:
|
|||
|
||||
```elixir
|
||||
defmodule MyApp.Reaction do
|
||||
# ...
|
||||
actions do
|
||||
read :for_comments do
|
||||
prepare set_context(%{data_layer: %{table: "comment_reactions"}})
|
||||
|
|
|
@ -68,7 +68,7 @@ import Config
|
|||
# This should already have been added in the first
|
||||
# getting started guide
|
||||
config :helpdesk,
|
||||
ash_apis: [Helpdesk.Support]
|
||||
ash_domains: [Helpdesk.Support]
|
||||
|
||||
config :helpdesk,
|
||||
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
|
||||
|
||||
use Ash.Resource,
|
||||
domain: Helpdesk.Support,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
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
|
||||
|
||||
use Ash.Resource,
|
||||
domain: Helpdesk.Support,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
|
110
lib/aggregate.ex
110
lib/aggregate.ex
|
@ -499,8 +499,7 @@ defmodule AshPostgres.Aggregate do
|
|||
end
|
||||
|
||||
defp resource_aggregates_to_aggregates(resource, aggregates) do
|
||||
aggregates
|
||||
|> Enum.reduce_while({:ok, []}, fn
|
||||
Enum.reduce_while(aggregates, {:ok, []}, fn
|
||||
%Ash.Query.Aggregate{} = aggregate, {:ok, aggregates} ->
|
||||
{:cont, {:ok, [aggregate | aggregates]}}
|
||||
|
||||
|
@ -523,6 +522,8 @@ defmodule AshPostgres.Aggregate do
|
|||
default: aggregate.default,
|
||||
filterable?: aggregate.filterable?,
|
||||
type: aggregate.type,
|
||||
sortable?: aggregate.filterable?,
|
||||
include_nil?: aggregate.include_nil?,
|
||||
constraints: aggregate.constraints,
|
||||
implementation: aggregate.implementation,
|
||||
uniq?: aggregate.uniq?,
|
||||
|
@ -685,11 +686,11 @@ defmodule AshPostgres.Aggregate do
|
|||
type: type,
|
||||
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 =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
Ash.Expr.fill_template(
|
||||
expression,
|
||||
aggregate.context[:actor],
|
||||
aggregate.context,
|
||||
|
@ -719,11 +720,11 @@ defmodule AshPostgres.Aggregate do
|
|||
expression = module.expression(opts, aggregate.context)
|
||||
|
||||
expression =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
Ash.Expr.fill_template(
|
||||
expression,
|
||||
aggregate.context[:actor],
|
||||
aggregate.context,
|
||||
aggregate.context
|
||||
aggregate.context.actor,
|
||||
aggregate.context.arguments,
|
||||
aggregate.context.source_context
|
||||
)
|
||||
|
||||
{:ok, expression} =
|
||||
|
@ -1189,6 +1190,13 @@ defmodule AshPostgres.Aggregate do
|
|||
|
||||
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} =
|
||||
if has_sort? || first_relationship.sort not in [nil, []] do
|
||||
{sort, binding} =
|
||||
|
@ -1211,31 +1219,63 @@ defmodule AshPostgres.Aggregate do
|
|||
:return
|
||||
)
|
||||
|
||||
question_marks = Enum.map(sort_expr, fn _ -> " ? " end)
|
||||
if aggregate.include_nil? do
|
||||
question_marks = Enum.map(sort_expr, fn _ -> " ? " end)
|
||||
|
||||
{:ok, expr} =
|
||||
AshPostgres.Functions.Fragment.casted_new(
|
||||
["array_agg(? ORDER BY #{question_marks})", field] ++ sort_expr
|
||||
)
|
||||
{:ok, expr} =
|
||||
Ash.Query.Function.Fragment.casted_new(
|
||||
["#{array_agg}(? ORDER BY #{question_marks} FILTER (WHERE ? IS NOT NULL))", field] ++
|
||||
sort_expr ++ [field]
|
||||
)
|
||||
|
||||
{sort_expr, acc} =
|
||||
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
||||
{sort_expr, acc} =
|
||||
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
||||
|
||||
query =
|
||||
AshPostgres.DataLayer.merge_expr_accumulator(query, acc)
|
||||
query =
|
||||
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
|
||||
{Ecto.Query.dynamic(
|
||||
[row],
|
||||
fragment("array_agg(?)", ^field)
|
||||
), query}
|
||||
case array_agg do
|
||||
"array_agg" ->
|
||||
{Ecto.Query.dynamic(
|
||||
[row],
|
||||
fragment("array_agg(?)", ^field)
|
||||
), query}
|
||||
|
||||
"any_value" ->
|
||||
{Ecto.Query.dynamic(
|
||||
[row],
|
||||
fragment("any_value(?)", ^field)
|
||||
), query}
|
||||
end
|
||||
end
|
||||
|
||||
{query, filtered} =
|
||||
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 =
|
||||
if aggregate.default_value do
|
||||
|
@ -1327,10 +1367,26 @@ defmodule AshPostgres.Aggregate do
|
|||
""
|
||||
end
|
||||
|
||||
{:ok, expr} =
|
||||
AshPostgres.Functions.Fragment.casted_new(
|
||||
["array_agg(#{distinct}? ORDER BY #{question_marks})", field] ++ sort_expr
|
||||
)
|
||||
expr =
|
||||
if aggregate.include_nil? do
|
||||
{: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} =
|
||||
AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false)
|
||||
|
|
|
@ -73,10 +73,11 @@ defmodule AshPostgres.Calculation do
|
|||
expression =
|
||||
Ash.Actions.Read.add_calc_context_to_filter(
|
||||
expression,
|
||||
calculation.context[:actor],
|
||||
calculation.context[:authorize?],
|
||||
calculation.context[:tenant],
|
||||
calculation.context[:tracer]
|
||||
calculation.context.actor,
|
||||
calculation.context.authorize?,
|
||||
calculation.context.tenant,
|
||||
calculation.context.tracer,
|
||||
nil
|
||||
)
|
||||
|
||||
{expr, acc} =
|
||||
|
|
|
@ -384,11 +384,12 @@ defmodule AshPostgres.DataLayer do
|
|||
|
||||
use Spark.Dsl.Extension,
|
||||
sections: @sections,
|
||||
transformers: [
|
||||
AshPostgres.Transformers.ValidateReferences,
|
||||
AshPostgres.Transformers.EnsureTableOrPolymorphic,
|
||||
AshPostgres.Transformers.PreventMultidimensionalArrayAggregates,
|
||||
AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchType
|
||||
verifiers: [
|
||||
AshPostgres.Verifiers.VerifyPostgresVersion,
|
||||
AshPostgres.Verifiers.PreventMultidimensionalArrayAggregates,
|
||||
AshPostgres.Verifiers.ValidateReferences,
|
||||
AshPostgres.Verifiers.PreventAttributeMultitenancyAndNonFullMatchType,
|
||||
AshPostgres.Verifiers.EnsureTableOrPolymorphic
|
||||
]
|
||||
|
||||
def migrate(args) do
|
||||
|
@ -704,7 +705,7 @@ defmodule AshPostgres.DataLayer do
|
|||
repo = dynamic_repo(resource, query)
|
||||
|
||||
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
|
||||
rescue
|
||||
|
@ -715,7 +716,7 @@ defmodule AshPostgres.DataLayer do
|
|||
defp no_table?(%{from: %{source: {"", _}}}), do: true
|
||||
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
|
||||
[prefix: schema]
|
||||
else
|
||||
|
@ -724,9 +725,9 @@ defmodule AshPostgres.DataLayer do
|
|||
|> add_timeout(timeout)
|
||||
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
|
||||
[prefix: tenant]
|
||||
[prefix: Ash.ToTenant.to_tenant(resource, tenant)]
|
||||
else
|
||||
if schema = AshPostgres.DataLayer.Info.schema(resource) do
|
||||
[prefix: schema]
|
||||
|
@ -748,7 +749,6 @@ defmodule AshPostgres.DataLayer do
|
|||
config = AshPostgres.DataLayer.Info.repo(resource, :mutate).config()
|
||||
|
||||
functions = [
|
||||
AshPostgres.Functions.Fragment,
|
||||
AshPostgres.Functions.Like,
|
||||
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
|
||||
|
||||
{:ok, add_single_aggs(result, resource, original_query, cant_group)}
|
||||
|
@ -880,10 +881,12 @@ defmodule AshPostgres.DataLayer do
|
|||
|> Ecto.Query.select(%{})
|
||||
end
|
||||
|
||||
repo = dynamic_repo(resource, filtered)
|
||||
|
||||
Map.put(
|
||||
result || %{},
|
||||
agg.name,
|
||||
dynamic_repo(resource, filtered).exists?(filtered, repo_opts(nil, nil, resource))
|
||||
repo.exists?(filtered, repo_opts(repo, nil, nil, resource))
|
||||
)
|
||||
|
||||
agg, result ->
|
||||
|
@ -953,9 +956,11 @@ defmodule AshPostgres.DataLayer do
|
|||
first_relationship
|
||||
)
|
||||
|
||||
repo = dynamic_repo(resource, query)
|
||||
|
||||
Map.merge(
|
||||
result || %{},
|
||||
dynamic_repo(resource, query).one(query, repo_opts(nil, nil, resource))
|
||||
repo.one(query, repo_opts(repo, nil, nil, resource))
|
||||
)
|
||||
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,
|
||||
repo_opts(nil, nil, source_resource)
|
||||
repo_opts(repo, nil, nil, source_resource)
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -1082,10 +1089,12 @@ defmodule AshPostgres.DataLayer do
|
|||
|> elem(0)
|
||||
|> Map.get(:resource)
|
||||
|
||||
repo = dynamic_repo(source_resource, lateral_join_query)
|
||||
|
||||
results =
|
||||
dynamic_repo(source_resource, lateral_join_query).all(
|
||||
repo.all(
|
||||
lateral_join_query,
|
||||
repo_opts(nil, nil, source_resource)
|
||||
repo_opts(repo, nil, nil, source_resource)
|
||||
)
|
||||
|> remap_mapped_fields(query)
|
||||
|
||||
|
@ -1357,7 +1366,8 @@ defmodule AshPostgres.DataLayer do
|
|||
|
||||
@doc false
|
||||
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
|
||||
%{__ash_bindings__: %{context: %{data_layer: %{schema: schema}}}} when not is_nil(schema) ->
|
||||
|
@ -1375,20 +1385,16 @@ defmodule AshPostgres.DataLayer do
|
|||
%{
|
||||
data_layer_query
|
||||
| prefix:
|
||||
to_string(
|
||||
query_tenant || AshPostgres.DataLayer.Info.schema(resource) ||
|
||||
config[:default_prefix] ||
|
||||
"public"
|
||||
)
|
||||
query_tenant || AshPostgres.DataLayer.Info.schema(resource) ||
|
||||
config[:default_prefix] ||
|
||||
"public"
|
||||
}
|
||||
else
|
||||
%{
|
||||
data_layer_query
|
||||
| prefix:
|
||||
to_string(
|
||||
AshPostgres.DataLayer.Info.schema(resource) || config[:default_prefix] ||
|
||||
"public"
|
||||
)
|
||||
AshPostgres.DataLayer.Info.schema(resource) || config[:default_prefix] ||
|
||||
"public"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -1418,7 +1424,7 @@ defmodule AshPostgres.DataLayer do
|
|||
data
|
||||
end
|
||||
|> 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
|
||||
{:error, error} ->
|
||||
|
@ -1427,12 +1433,12 @@ defmodule AshPostgres.DataLayer do
|
|||
{:ok, query} ->
|
||||
try do
|
||||
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(
|
||||
resource,
|
||||
query,
|
||||
ecto_changeset.filters,
|
||||
changeset.filter,
|
||||
changeset.atomics,
|
||||
ecto_changeset.changes,
|
||||
[]
|
||||
|
@ -1582,7 +1588,7 @@ defmodule AshPostgres.DataLayer do
|
|||
data
|
||||
end
|
||||
|> Map.update!(:__meta__, &Map.put(&1, :source, table(resource, changeset)))
|
||||
|> ecto_changeset(changeset, :update, true, true)
|
||||
|> ecto_changeset(changeset, :update, true)
|
||||
|
||||
try do
|
||||
query =
|
||||
|
@ -1599,10 +1605,10 @@ defmodule AshPostgres.DataLayer do
|
|||
end
|
||||
|> Ecto.Query.exclude(:order_by)
|
||||
|
||||
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)
|
||||
|
||||
query =
|
||||
if Enum.any?(query.joins, &(&1.qual != :inner)) do
|
||||
root_query =
|
||||
|
@ -1663,7 +1669,11 @@ defmodule AshPostgres.DataLayer do
|
|||
|
||||
@impl true
|
||||
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 =
|
||||
if options.return_records? do
|
||||
|
@ -1672,8 +1682,6 @@ defmodule AshPostgres.DataLayer do
|
|||
opts
|
||||
end
|
||||
|
||||
changesets = Enum.to_list(stream)
|
||||
repo = dynamic_repo(resource, Enum.at(changesets, 0))
|
||||
source = resolve_source(resource, Enum.at(changesets, 0))
|
||||
|
||||
try do
|
||||
|
@ -1681,7 +1689,7 @@ defmodule AshPostgres.DataLayer do
|
|||
if options[:upsert?] do
|
||||
# Ash groups changesets by atomics before dispatching them to the data layer
|
||||
# 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)
|
||||
|
||||
|
@ -1696,7 +1704,7 @@ defmodule AshPostgres.DataLayer do
|
|||
case query_with_atomics(
|
||||
resource,
|
||||
query,
|
||||
filters,
|
||||
filter,
|
||||
atomics,
|
||||
%{},
|
||||
upsert_set
|
||||
|
@ -1967,10 +1975,6 @@ defmodule AshPostgres.DataLayer do
|
|||
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
|
||||
Ash.Error.Changes.InvalidAttribute.exception(
|
||||
field: field,
|
||||
|
@ -1979,25 +1983,7 @@ defmodule AshPostgres.DataLayer do
|
|||
)
|
||||
end
|
||||
|
||||
defp ecto_changeset(record, changeset, type, table_error? \\ true, bulk_update? \\ false) 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
|
||||
|
||||
defp ecto_changeset(record, changeset, type, table_error? \\ true) do
|
||||
attributes =
|
||||
changeset.resource
|
||||
|> Ash.Resource.Info.attributes()
|
||||
|
@ -2013,7 +1999,6 @@ defmodule AshPostgres.DataLayer do
|
|||
|> to_ecto()
|
||||
|> set_table(changeset, type, table_error?)
|
||||
|> 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_unique_indexes(record.__struct__, changeset)
|
||||
|> add_check_constraints(record.__struct__)
|
||||
|
@ -2615,7 +2600,11 @@ defmodule AshPostgres.DataLayer do
|
|||
|> ecto_changeset(changeset, :update)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -2630,7 +2619,7 @@ defmodule AshPostgres.DataLayer do
|
|||
case query_with_atomics(
|
||||
resource,
|
||||
query,
|
||||
ecto_changeset.filters,
|
||||
changeset.filter,
|
||||
changeset.atomics,
|
||||
ecto_changeset.changes,
|
||||
[]
|
||||
|
@ -2639,13 +2628,12 @@ defmodule AshPostgres.DataLayer do
|
|||
{:ok, changeset.data}
|
||||
|
||||
{: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 =
|
||||
Keyword.put(repo_opts, :returning, Keyword.keys(changeset.atomics))
|
||||
|
||||
repo = dynamic_repo(resource, changeset)
|
||||
|
||||
result =
|
||||
with_savepoint(repo, query, fn ->
|
||||
repo.update_all(
|
||||
|
@ -2660,7 +2648,7 @@ defmodule AshPostgres.DataLayer do
|
|||
{:error,
|
||||
Ash.Error.Changes.StaleRecord.exception(
|
||||
resource: resource,
|
||||
filters: ecto_changeset.filters
|
||||
filters: changeset.filter
|
||||
)}
|
||||
|
||||
{1, [result]} ->
|
||||
|
@ -2684,20 +2672,29 @@ defmodule AshPostgres.DataLayer do
|
|||
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(
|
||||
resource,
|
||||
query,
|
||||
filters,
|
||||
filter,
|
||||
atomics,
|
||||
updating_one_changes,
|
||||
existing_set
|
||||
) do
|
||||
query =
|
||||
Enum.reduce(filters, query, fn {key, value}, query ->
|
||||
from(row in query,
|
||||
where: field(row, ^key) == ^value
|
||||
)
|
||||
end)
|
||||
if is_nil(filter) do
|
||||
query
|
||||
else
|
||||
filter(query, filter, resource)
|
||||
end
|
||||
|
||||
atomics_result =
|
||||
Enum.reduce_while(atomics, {:ok, query, []}, fn {field, expr}, {:ok, query, set} ->
|
||||
|
@ -2792,20 +2789,23 @@ defmodule AshPostgres.DataLayer do
|
|||
try do
|
||||
repo = dynamic_repo(resource, changeset)
|
||||
|
||||
result =
|
||||
repo.delete(
|
||||
ecto_changeset,
|
||||
repo_opts(changeset.timeout, changeset.tenant, changeset.resource)
|
||||
)
|
||||
source = resolve_source(resource, changeset)
|
||||
|
||||
result
|
||||
|> from_ecto()
|
||||
from(row in source, as: ^0)
|
||||
|> default_bindings(resource, changeset.context)
|
||||
|> filter(changeset.filter, resource)
|
||||
|> case do
|
||||
{:ok, _record} ->
|
||||
{:ok, query} ->
|
||||
query
|
||||
|> pkey_filter(record)
|
||||
|> repo.delete_all(
|
||||
repo_opts(repo, changeset.timeout, changeset.tenant, changeset.resource)
|
||||
)
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
handle_errors({:error, error})
|
||||
{:error, error}
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
|
@ -3201,9 +3201,13 @@ defmodule AshPostgres.DataLayer do
|
|||
|> Enum.reduce(query, fn filter, query ->
|
||||
{dynamic, acc} = AshPostgres.Expr.dynamic_expr(query, filter, query.__ash_bindings__)
|
||||
|
||||
query
|
||||
|> Ecto.Query.where(^dynamic)
|
||||
|> merge_expr_accumulator(acc)
|
||||
if is_nil(dynamic) do
|
||||
query
|
||||
else
|
||||
query
|
||||
|> Ecto.Query.where([], ^dynamic)
|
||||
|> merge_expr_accumulator(acc)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
@ -14,6 +14,21 @@ defmodule AshPostgres.DataLayer.Info do
|
|||
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"
|
||||
def table(resource) do
|
||||
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,
|
||||
DateTimeAdd,
|
||||
Error,
|
||||
Fragment,
|
||||
FromNow,
|
||||
GetPath,
|
||||
If,
|
||||
|
@ -21,6 +22,7 @@ defmodule AshPostgres.Expr do
|
|||
Length,
|
||||
Now,
|
||||
Round,
|
||||
StringDowncase,
|
||||
StringJoin,
|
||||
StringLength,
|
||||
StringSplit,
|
||||
|
@ -29,7 +31,7 @@ defmodule AshPostgres.Expr do
|
|||
Type
|
||||
}
|
||||
|
||||
alias AshPostgres.Functions.{Fragment, ILike, Like, TrigramSimilarity, VectorCosineDistance}
|
||||
alias AshPostgres.Functions.{ILike, Like, TrigramSimilarity, VectorCosineDistance}
|
||||
|
||||
require Ecto.Query
|
||||
|
||||
|
@ -725,6 +727,27 @@ defmodule AshPostgres.Expr do
|
|||
)
|
||||
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(
|
||||
query,
|
||||
%StringTrim{arguments: [value], embedded?: pred_embedded?},
|
||||
|
@ -1074,10 +1097,11 @@ defmodule AshPostgres.Expr do
|
|||
expression =
|
||||
Ash.Actions.Read.add_calc_context_to_filter(
|
||||
expression,
|
||||
calculation.context[:actor],
|
||||
calculation.context[:authorize?],
|
||||
calculation.context[:tenant],
|
||||
calculation.context[:tracer]
|
||||
calculation.context.actor,
|
||||
calculation.context.authorize?,
|
||||
calculation.context.tenant,
|
||||
calculation.context.tracer,
|
||||
nil
|
||||
)
|
||||
|
||||
do_dynamic_expr(
|
||||
|
@ -1190,7 +1214,8 @@ defmodule AshPostgres.Expr do
|
|||
aggregate.context[:actor],
|
||||
aggregate.context[:authorize?],
|
||||
aggregate.context[:tenant],
|
||||
aggregate.context[:tracer]
|
||||
aggregate.context[:tracer],
|
||||
nil
|
||||
)
|
||||
|
||||
{value, acc} = do_dynamic_expr(query, ref, query.__ash_bindings__, false, acc)
|
||||
|
@ -1452,10 +1477,10 @@ defmodule AshPostgres.Expr do
|
|||
end
|
||||
|
||||
{encoded, acc} =
|
||||
if Ash.Filter.TemplateHelpers.expr?(input) do
|
||||
if Ash.Expr.expr?(input) do
|
||||
frag_parts =
|
||||
Enum.flat_map(input, fn {key, value} ->
|
||||
if Ash.Filter.TemplateHelpers.expr?(value) do
|
||||
if Ash.Expr.expr?(value) do
|
||||
[
|
||||
expr: to_string(key),
|
||||
raw: "::text, ",
|
||||
|
@ -1720,7 +1745,7 @@ defmodule AshPostgres.Expr do
|
|||
when is_map(value) and not is_struct(value) do
|
||||
if bindings[:location] == :update &&
|
||||
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
|
||||
elements =
|
||||
value
|
||||
|
@ -1770,7 +1795,7 @@ defmodule AshPostgres.Expr do
|
|||
if other && is_atom(other) && !is_boolean(other) do
|
||||
{to_string(other), acc}
|
||||
else
|
||||
if Ash.Filter.TemplateHelpers.expr?(other) do
|
||||
if Ash.Expr.expr?(other) do
|
||||
if is_list(other) do
|
||||
list_expr(query, other, bindings, true, acc, type)
|
||||
else
|
||||
|
@ -1801,7 +1826,7 @@ defmodule AshPostgres.Expr do
|
|||
end
|
||||
|
||||
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
|
||||
list_expr(query, value, bindings, false, acc, type)
|
||||
else
|
||||
|
@ -2033,7 +2058,7 @@ defmodule AshPostgres.Expr do
|
|||
defp list_expr(query, value, bindings, embedded?, acc, type) do
|
||||
if !Enum.empty?(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
|
||||
type =
|
||||
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,
|
||||
drop_columns: false
|
||||
|
||||
def generate(apis, opts \\ []) do
|
||||
apis = List.wrap(apis)
|
||||
def generate(domains, opts \\ []) do
|
||||
domains = List.wrap(domains)
|
||||
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} =
|
||||
all_resources
|
||||
|
@ -60,8 +60,8 @@ defmodule AshPostgres.MigrationGenerator do
|
|||
|
||||
Does not support everything supported by the migration generator.
|
||||
"""
|
||||
def take_snapshots(api, repo, only_resources \\ nil) do
|
||||
all_resources = api |> Ash.Api.Info.resources() |> Enum.uniq()
|
||||
def take_snapshots(domain, repo, only_resources \\ nil) do
|
||||
all_resources = domain |> Ash.Domain.Info.resources() |> Enum.uniq()
|
||||
|
||||
all_resources
|
||||
|> Enum.filter(fn resource ->
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.MixHelpers do
|
||||
@moduledoc false
|
||||
def apis!(opts, args) do
|
||||
def domains!(opts, args) do
|
||||
apps =
|
||||
if apps_paths = Mix.Project.apps_paths() do
|
||||
apps_paths |> Map.keys() |> Enum.sort()
|
||||
|
@ -8,46 +8,46 @@ defmodule AshPostgres.MixHelpers do
|
|||
[Mix.Project.config()[:app]]
|
||||
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 =
|
||||
if opts[:apis] && opts[:apis] != "" do
|
||||
opts[:apis]
|
||||
domains =
|
||||
if opts[:domains] && opts[:domains] != "" do
|
||||
opts[:domains]
|
||||
|> Kernel.||("")
|
||||
|> String.split(",")
|
||||
|> Enum.flat_map(fn
|
||||
"" ->
|
||||
[]
|
||||
|
||||
api ->
|
||||
[Module.concat([api])]
|
||||
domain ->
|
||||
[Module.concat([domain])]
|
||||
end)
|
||||
else
|
||||
configured_apis
|
||||
configure_domains
|
||||
end
|
||||
|
||||
apis
|
||||
domains
|
||||
|> Enum.map(&ensure_compiled(&1, args))
|
||||
|> 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 ->
|
||||
apis
|
||||
domains ->
|
||||
domains
|
||||
end
|
||||
end
|
||||
|
||||
def repos!(opts, args) do
|
||||
apis = apis!(opts, args)
|
||||
domains = domains!(opts, args)
|
||||
|
||||
resources =
|
||||
apis
|
||||
|> Enum.flat_map(&Ash.Api.Info.resources/1)
|
||||
domains
|
||||
|> Enum.flat_map(&Ash.Domain.Info.resources/1)
|
||||
|> Enum.filter(&(Ash.DataLayer.data_layer(&1) == AshPostgres.DataLayer))
|
||||
|> case do
|
||||
[] ->
|
||||
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`.
|
||||
"""
|
||||
|
@ -64,7 +64,7 @@ defmodule AshPostgres.MixHelpers do
|
|||
|> case do
|
||||
[] ->
|
||||
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.
|
||||
|
||||
|
@ -98,7 +98,7 @@ defmodule AshPostgres.MixHelpers do
|
|||
end
|
||||
end
|
||||
|
||||
defp ensure_compiled(api, args) do
|
||||
defp ensure_compiled(domain, args) do
|
||||
if Code.ensure_loaded?(Mix.Tasks.App.Config) do
|
||||
Mix.Task.run("app.config", args)
|
||||
else
|
||||
|
@ -106,18 +106,18 @@ defmodule AshPostgres.MixHelpers do
|
|||
"--no-compile" not in args && Mix.Task.run("compile", args)
|
||||
end
|
||||
|
||||
case Code.ensure_compiled(api) do
|
||||
case Code.ensure_compiled(domain) do
|
||||
{:module, _} ->
|
||||
api
|
||||
|> Ash.Api.Info.resources()
|
||||
domain
|
||||
|> Ash.Domain.Info.resources()
|
||||
|> Enum.each(&Code.ensure_compiled/1)
|
||||
|
||||
# TODO: We shouldn't need to make sure that the resources are compiled
|
||||
|
||||
api
|
||||
domain
|
||||
|
||||
{:error, error} ->
|
||||
Mix.raise("Could not load #{inspect(api)}, error: #{inspect(error)}. ")
|
||||
Mix.raise("Could not load #{inspect(domain)}, error: #{inspect(error)}. ")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ defmodule Mix.Tasks.AshPostgres.Create do
|
|||
|
||||
@switches [
|
||||
quiet: :boolean,
|
||||
apis: :string,
|
||||
domains: :string,
|
||||
no_compile: :boolean,
|
||||
no_deps_check: :boolean
|
||||
]
|
||||
|
@ -15,16 +15,16 @@ defmodule Mix.Tasks.AshPostgres.Create do
|
|||
]
|
||||
|
||||
@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
|
||||
|
||||
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
|
||||
|
||||
* `--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
|
||||
* `--no-compile` - 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)]
|
||||
end)
|
||||
|
||||
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--apis")
|
||||
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--domains")
|
||||
|
||||
Mix.Task.reenable("ecto.create")
|
||||
Mix.Task.run("ecto.create", repo_args ++ rest_opts)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule Mix.Tasks.AshPostgres.Drop do
|
||||
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]
|
||||
|
||||
@aliases [
|
||||
|
@ -13,7 +13,7 @@ defmodule Mix.Tasks.AshPostgres.Drop do
|
|||
force: :boolean,
|
||||
force_drop: :boolean,
|
||||
quiet: :boolean,
|
||||
apis: :string,
|
||||
domains: :string,
|
||||
no_compile: :boolean,
|
||||
no_deps_check: :boolean
|
||||
]
|
||||
|
@ -24,11 +24,11 @@ defmodule Mix.Tasks.AshPostgres.Drop do
|
|||
## Examples
|
||||
|
||||
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
|
||||
|
||||
* `--apis` - the apis who's repos should be dropped
|
||||
* `--domains` - the domains who's repos should be dropped
|
||||
* `-q`, `--quiet` - run the command quietly
|
||||
* `-f`, `--force` - do not ask for confirmation when dropping the database.
|
||||
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)]
|
||||
end)
|
||||
|
||||
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--apis")
|
||||
rest_opts = AshPostgres.MixHelpers.delete_arg(args, "--domains")
|
||||
|
||||
Mix.Task.reenable("ecto.drop")
|
||||
Mix.Task.run("ecto.drop", repo_args ++ rest_opts)
|
||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
|||
|
||||
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"
|
||||
* `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`
|
||||
|
@ -87,7 +87,7 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
|||
{opts, _} =
|
||||
OptionParser.parse!(args,
|
||||
strict: [
|
||||
apis: :string,
|
||||
domains: :string,
|
||||
snapshot_path: :string,
|
||||
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
|
||||
|
@ -119,6 +119,6 @@ defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
|||
""")
|
||||
end
|
||||
|
||||
AshPostgres.MigrationGenerator.generate(apis, opts)
|
||||
AshPostgres.MigrationGenerator.generate(domains, opts)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
|||
import AshPostgres.MixHelpers,
|
||||
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 [
|
||||
n: :step
|
||||
|
@ -20,7 +20,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
|||
pool_size: :integer,
|
||||
log_sql: :boolean,
|
||||
strict_version_order: :boolean,
|
||||
apis: :string,
|
||||
domains: :string,
|
||||
no_compile: :boolean,
|
||||
no_deps_check: :boolean,
|
||||
migrations_path: :keep,
|
||||
|
@ -42,7 +42,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
|||
specific version number, supply `--to version_number`. To migrate a
|
||||
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
|
||||
migrate/roll them back to different points, you will need to use the
|
||||
ecto specific task, `mix ecto.migrate` and provide your repo name.
|
||||
|
@ -53,7 +53,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
|||
## Examples
|
||||
|
||||
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 --step 3
|
||||
|
@ -62,7 +62,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
|||
|
||||
## 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
|
||||
|
||||
|
@ -107,7 +107,7 @@ defmodule Mix.Tasks.AshPostgres.Migrate do
|
|||
|
||||
rest_opts =
|
||||
args
|
||||
|> AshPostgres.MixHelpers.delete_arg("--apis")
|
||||
|> AshPostgres.MixHelpers.delete_arg("--domains")
|
||||
|> AshPostgres.MixHelpers.delete_arg("--migrations-path")
|
||||
|> AshPostgres.MixHelpers.delete_flag("--tenants")
|
||||
|> AshPostgres.MixHelpers.delete_flag("--only-tenants")
|
||||
|
|
|
@ -4,7 +4,7 @@ defmodule Mix.Tasks.AshPostgres.Rollback do
|
|||
import AshPostgres.MixHelpers,
|
||||
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 """
|
||||
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
|
||||
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
|
||||
migrate/roll them back to different points, you will need to use the
|
||||
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
|
||||
|
||||
## 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
|
||||
* `--step` / `-n` - revert n number of applied migrations
|
||||
* `--to` / `-v` - revert all migrations down to and including version
|
||||
|
@ -66,7 +66,7 @@ defmodule Mix.Tasks.AshPostgres.Rollback do
|
|||
|
||||
rest_opts =
|
||||
args
|
||||
|> AshPostgres.MixHelpers.delete_arg("--apis")
|
||||
|> AshPostgres.MixHelpers.delete_arg("--domains")
|
||||
|> AshPostgres.MixHelpers.delete_arg("--migrations-path")
|
||||
|> AshPostgres.MixHelpers.delete_flag("--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"
|
||||
@callback installed_extensions() :: [String.t() | module()]
|
||||
|
||||
@doc "Configure the version of postgres that is being used."
|
||||
@callback pg_version() :: Version.t()
|
||||
|
||||
@doc """
|
||||
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,
|
||||
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
|
||||
|
||||
|
@ -66,6 +68,7 @@ defmodule AshPostgres.Repo do
|
|||
@callback migrations_path() :: String.t() | nil
|
||||
@doc "The default prefix(postgres schema) to use when building queries"
|
||||
@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"
|
||||
@callback override_migration_type(atom) :: atom
|
||||
|
||||
|
@ -88,7 +91,6 @@ defmodule AshPostgres.Repo do
|
|||
def migrations_path, do: nil
|
||||
def default_prefix, do: "public"
|
||||
def override_migration_type(type), do: type
|
||||
def min_pg_version, do: 10
|
||||
|
||||
def transaction!(fun) do
|
||||
case fun.() do
|
||||
|
@ -224,8 +226,7 @@ defmodule AshPostgres.Repo do
|
|||
all_tenants: 0,
|
||||
tenant_migrations_path: 0,
|
||||
default_prefix: 0,
|
||||
override_migration_type: 1,
|
||||
min_pg_version: 0
|
||||
override_migration_type: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
defmodule AshPostgres.Transformers.EnsureTableOrPolymorphic do
|
||||
defmodule AshPostgres.Verifiers.EnsureTableOrPolymorphic do
|
||||
@moduledoc false
|
||||
use Spark.Dsl.Transformer
|
||||
alias Spark.Dsl.Transformer
|
||||
use Spark.Dsl.Verifier
|
||||
alias Spark.Dsl.Verifier
|
||||
|
||||
def transform(dsl) do
|
||||
if Transformer.get_option(dsl, [:postgres], :polymorphic?) ||
|
||||
Transformer.get_option(dsl, [:postgres], :table) do
|
||||
{:ok, dsl}
|
||||
def verify(dsl) do
|
||||
if Verifier.get_option(dsl, [:postgres], :polymorphic?) ||
|
||||
Verifier.get_option(dsl, [:postgres], :table) do
|
||||
:ok
|
||||
else
|
||||
resource = Transformer.get_persisted(dsl, :module)
|
||||
resource = Verifier.get_persisted(dsl, :module)
|
||||
|
||||
raise Spark.Error.DslError,
|
||||
module: resource,
|
|
@ -1,10 +1,10 @@
|
|||
defmodule AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchType do
|
||||
defmodule AshPostgres.Verifiers.PreventAttributeMultitenancyAndNonFullMatchType do
|
||||
@moduledoc false
|
||||
use Spark.Dsl.Transformer
|
||||
alias Spark.Dsl.Transformer
|
||||
use Spark.Dsl.Verifier
|
||||
alias Spark.Dsl.Verifier
|
||||
|
||||
def transform(dsl) do
|
||||
if Transformer.get_option(dsl, [:multitenancy], :strategy) == :attribute do
|
||||
def verify(dsl) do
|
||||
if Verifier.get_option(dsl, [:multitenancy], :strategy) == :attribute do
|
||||
dsl
|
||||
|> AshPostgres.DataLayer.Info.references()
|
||||
|> Enum.filter(&(&1.match_type && &1.match_type != :full))
|
||||
|
@ -14,7 +14,7 @@ defmodule AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchTy
|
|||
if uses_attribute_strategy?(relationship) and
|
||||
not targets_primary_key?(relationship) and
|
||||
not targets_multitenancy_attribute?(relationship) do
|
||||
resource = Transformer.get_persisted(dsl, :module)
|
||||
resource = Verifier.get_persisted(dsl, :module)
|
||||
|
||||
raise Spark.Error.DslError,
|
||||
module: resource,
|
||||
|
@ -28,9 +28,9 @@ defmodule AshPostgres.Transformers.PreventAttributeMultitenancyAndNonFullMatchTy
|
|||
:ok
|
||||
end
|
||||
end)
|
||||
else
|
||||
{:ok, dsl}
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp uses_attribute_strategy?(relationship) do
|
|
@ -1,12 +1,10 @@
|
|||
defmodule AshPostgres.Transformers.PreventMultidimensionalArrayAggregates do
|
||||
defmodule AshPostgres.Verifiers.PreventMultidimensionalArrayAggregates do
|
||||
@moduledoc false
|
||||
use Spark.Dsl.Transformer
|
||||
alias Spark.Dsl.Transformer
|
||||
use Spark.Dsl.Verifier
|
||||
alias Spark.Dsl.Verifier
|
||||
|
||||
def after_compile?, do: true
|
||||
|
||||
def transform(dsl) do
|
||||
resource = Transformer.get_persisted(dsl, :module)
|
||||
def verify(dsl) do
|
||||
resource = Verifier.get_persisted(dsl, :module)
|
||||
|
||||
dsl
|
||||
|> Ash.Resource.Info.aggregates()
|
||||
|
@ -35,6 +33,6 @@ defmodule AshPostgres.Transformers.PreventMultidimensionalArrayAggregates do
|
|||
end
|
||||
end)
|
||||
|
||||
{:ok, dsl}
|
||||
:ok
|
||||
end
|
||||
end
|
|
@ -1,23 +1,21 @@
|
|||
defmodule AshPostgres.Transformers.ValidateReferences do
|
||||
defmodule AshPostgres.Verifiers.ValidateReferences do
|
||||
@moduledoc false
|
||||
use Spark.Dsl.Transformer
|
||||
alias Spark.Dsl.Transformer
|
||||
use Spark.Dsl.Verifier
|
||||
alias Spark.Dsl.Verifier
|
||||
|
||||
def after_compile?, do: true
|
||||
|
||||
def transform(dsl) do
|
||||
def verify(dsl) do
|
||||
dsl
|
||||
|> AshPostgres.DataLayer.Info.references()
|
||||
|> Enum.each(fn reference ->
|
||||
unless Ash.Resource.Info.relationship(dsl, reference.relationship) do
|
||||
raise Spark.Error.DslError,
|
||||
path: [:postgres, :references, reference.relationship],
|
||||
module: Transformer.get_persisted(dsl, :module),
|
||||
module: Verifier.get_persisted(dsl, :module),
|
||||
message:
|
||||
"Found reference configuration for relationship `#{reference.relationship}`, but no such relationship exists"
|
||||
end
|
||||
end)
|
||||
|
||||
{:ok, dsl}
|
||||
:ok
|
||||
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
|
||||
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, []}
|
||||
]
|
||||
end
|
||||
|
@ -140,7 +140,6 @@ defmodule AshPostgres.MixProject do
|
|||
EctoMigrationDefault
|
||||
],
|
||||
Expressions: [
|
||||
AshPostgres.Functions.Fragment,
|
||||
AshPostgres.Functions.TrigramSimilarity,
|
||||
AshPostgres.Functions.ILike,
|
||||
AshPostgres.Functions.Like,
|
||||
|
@ -157,7 +156,10 @@ defmodule AshPostgres.MixProject do
|
|||
{:ecto, "~> 3.9"},
|
||||
{:jason, "~> 1.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]},
|
||||
{:git_ops, "~> 2.5", only: [:dev, :test]},
|
||||
{: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"},
|
||||
"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"},
|
||||
|
@ -8,11 +8,9 @@
|
|||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
||||
"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"},
|
||||
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
|
||||
"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_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"},
|
||||
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
|
||||
"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_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"},
|
||||
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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"},
|
||||
"stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"},
|
||||
"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
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert_receive %{
|
||||
type: :create,
|
||||
|
@ -15,8 +15,8 @@ defmodule AshPostgresTest do
|
|||
test "filter policies are applied" do
|
||||
post =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Changeset.new(%{title: "good"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "good"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Forbidden, fn ->
|
||||
post
|
||||
|
@ -24,13 +24,13 @@ defmodule AshPostgresTest do
|
|||
authorize?: true,
|
||||
actor: %{id: Ash.UUID.generate()}
|
||||
)
|
||||
|> AshPostgres.Test.Api.update!()
|
||||
|> Ash.update!()
|
||||
|> Map.get(:title)
|
||||
end
|
||||
|
||||
post
|
||||
|> Ash.Changeset.for_update(:update, %{title: "okay"}, authorize?: true)
|
||||
|> AshPostgres.Test.Api.update!()
|
||||
|> Ash.update!()
|
||||
|> Map.get(:title)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.AtomicsTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
import Ash.Expr
|
||||
|
||||
|
@ -10,55 +10,57 @@ defmodule AshPostgres.AtomicsTest do
|
|||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{id: id, title: "foo", price: 1}, upsert?: true)
|
||||
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{id: id, title: "foo", price: 1}, upsert?: true)
|
||||
|> 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
|
||||
|
||||
test "a basic atomic works" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{price: 2} =
|
||||
post
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.Changeset.atomic_update(:price, expr(price + 1))
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
end
|
||||
|
||||
test "an atomic works with a datetime" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
now = DateTime.utc_now()
|
||||
|
||||
assert %{created_at: ^now} =
|
||||
post
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.atomic_update(:created_at, expr(^now))
|
||||
|> Api.update!()
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.update!()
|
||||
end
|
||||
|
||||
test "an atomic that violates a constraint will return the proper error" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Invalid, ~r/does not exist/, fn ->
|
||||
post
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.atomic_update(:organization_id, Ash.UUID.generate())
|
||||
|> Api.update!()
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.update!()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,13 +68,13 @@ defmodule AshPostgres.AtomicsTest do
|
|||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
post =
|
||||
post
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.Changeset.atomic_update(:score, expr(score_after_winning))
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
assert post.score == 1
|
||||
end
|
||||
|
@ -81,7 +83,7 @@ defmodule AshPostgres.AtomicsTest do
|
|||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", price: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert Post.increment_score!(post, 2).score == 2
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
defmodule AshPostgres.BulkCreateTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
describe "bulk creates" 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"}] =
|
||||
Post
|
||||
|> Ash.Query.sort(:title)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "bulk creates can be streamed" do
|
||||
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_records?: true
|
||||
)
|
||||
|
@ -26,7 +26,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
{:ok, %{title: "fred", uniq_one: "one", uniq_two: "two", price: 10}},
|
||||
{: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: "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: "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: "else", uniq_one: "three", uniq_two: "four", price: 20_000}
|
||||
|
@ -76,7 +76,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
# id: org_id,
|
||||
# title: "Avengers"
|
||||
# })
|
||||
# |> Api.create!()
|
||||
# |> Ash.create!()
|
||||
|
||||
# assert [
|
||||
# {:ok,
|
||||
|
@ -94,7 +94,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
# organization_id: org_id
|
||||
# }}
|
||||
# ] =
|
||||
# Api.bulk_create!(
|
||||
# Ash.bulk_create!(
|
||||
# [
|
||||
# %{
|
||||
# name: "Tony Stark",
|
||||
|
@ -135,7 +135,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
# role: "master in chief"
|
||||
# }}
|
||||
# ] =
|
||||
# Api.bulk_create!(
|
||||
# Ash.bulk_create!(
|
||||
# [
|
||||
# %{
|
||||
# name: "Tony Stark",
|
||||
|
@ -169,7 +169,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
# end
|
||||
|
||||
test "bulk creates can create relationships" do
|
||||
Api.bulk_create!(
|
||||
Ash.bulk_create!(
|
||||
[%{title: "fred", rating: %{score: 5}}, %{title: "george", rating: %{score: 0}}],
|
||||
Post,
|
||||
:create
|
||||
|
@ -182,14 +182,14 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
Post
|
||||
|> Ash.Query.sort(:title)
|
||||
|> Ash.Query.load(:ratings)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
describe "validation errors" do
|
||||
test "skips invalid by default" do
|
||||
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_errors?: true
|
||||
)
|
||||
|
@ -197,7 +197,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
|
||||
test "returns errors in the stream" do
|
||||
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_stream?: true,
|
||||
return_errors?: true
|
||||
|
@ -209,7 +209,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
describe "database errors" do
|
||||
test "database errors affect the entire batch" do
|
||||
# assert %{records: [_], errors: [_]} =
|
||||
Api.bulk_create(
|
||||
Ash.bulk_create(
|
||||
[%{title: "fred"}, %{title: "george", organization_id: Ash.UUID.generate()}],
|
||||
Post,
|
||||
:create,
|
||||
|
@ -219,11 +219,11 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
assert [] =
|
||||
Post
|
||||
|> Ash.Query.sort(:title)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "database errors don't affect other batches" do
|
||||
Api.bulk_create(
|
||||
Ash.bulk_create(
|
||||
[%{title: "george", organization_id: Ash.UUID.generate()}, %{title: "fred"}],
|
||||
Post,
|
||||
:create,
|
||||
|
@ -234,7 +234,7 @@ defmodule AshPostgres.BulkCreateTest do
|
|||
assert [%{title: "fred"}] =
|
||||
Post
|
||||
|> Ash.Query.sort(:title)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
defmodule AshPostgres.BulkDestroyTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Expr
|
||||
require Ash.Query
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|> Ash.Query.filter(title == "fred")
|
||||
|> Api.bulk_destroy!(:update, %{})
|
||||
|> Ash.bulk_destroy!(:update, %{})
|
||||
|
||||
# 😢 sad
|
||||
assert [%{title: "george"}] = Api.read!(Post)
|
||||
assert [%{title: "george"}] = Ash.read!(Post)
|
||||
end
|
||||
|
||||
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
|
||||
|> 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
|
||||
|
||||
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
|
||||
|> Api.read!()
|
||||
|> Api.bulk_destroy!(:destroy, %{})
|
||||
|> Ash.read!()
|
||||
|> Ash.bulk_destroy!(:destroy, %{}, strategy: :stream, return_errors?: true)
|
||||
|
||||
assert [] = Api.read!(Post)
|
||||
assert [] = Ash.read!(Post)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
defmodule AshPostgres.BulkUpdateTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Expr
|
||||
require Ash.Query
|
||||
|
||||
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
|
||||
|
||||
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")}
|
||||
)
|
||||
|
||||
posts = Api.read!(Post)
|
||||
posts = Ash.read!(Post)
|
||||
assert Enum.all?(posts, &String.ends_with?(&1.title, "_stuff"))
|
||||
end
|
||||
|
||||
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
|
||||
|> Api.bulk_update!(
|
||||
|> Ash.bulk_update!(
|
||||
:update,
|
||||
%{list_of_stuff: [%{a: 1}]},
|
||||
return_records?: true,
|
||||
|
@ -36,25 +36,25 @@ defmodule AshPostgres.BulkUpdateTest do
|
|||
|
||||
test "a map can be given as input on a regular update" do
|
||||
%{records: [post | _]} =
|
||||
Api.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create,
|
||||
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create,
|
||||
return_records?: true
|
||||
)
|
||||
|
||||
post
|
||||
|> Ash.Changeset.for_update(:update, %{list_of_stuff: [%{a: [:a, :b]}, %{a: [:c, :d]}]})
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
end
|
||||
|
||||
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
|
||||
|> 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 =
|
||||
Post
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.map(& &1.title)
|
||||
|> Enum.sort()
|
||||
|
||||
|
@ -62,15 +62,15 @@ defmodule AshPostgres.BulkUpdateTest do
|
|||
end
|
||||
|
||||
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
|
||||
|> 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 =
|
||||
Post
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.map(& &1.title)
|
||||
|> Enum.sort()
|
||||
|
||||
|
@ -78,18 +78,19 @@ defmodule AshPostgres.BulkUpdateTest do
|
|||
end
|
||||
|
||||
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
|
||||
|> Api.read!()
|
||||
|> Api.bulk_update!(:update, %{},
|
||||
|> Ash.read!()
|
||||
|> Ash.bulk_update!(:update, %{},
|
||||
atomic_update: %{title: Ash.Expr.expr(title <> "_stuff")},
|
||||
return_records?: true
|
||||
return_records?: true,
|
||||
strategy: [:stream]
|
||||
)
|
||||
|
||||
titles =
|
||||
Post
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.map(& &1.title)
|
||||
|> Enum.sort()
|
||||
|
||||
|
@ -97,14 +98,17 @@ defmodule AshPostgres.BulkUpdateTest do
|
|||
end
|
||||
|
||||
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
|
||||
|> Ash.Query.for_read(:paginated, authorize?: true)
|
||||
|> Api.bulk_update!(:requires_initial_data, %{},
|
||||
authorize?: true,
|
||||
allow_stream_with: :full_read,
|
||||
authorize_query?: false
|
||||
)
|
||||
assert_raise Ash.Error.Invalid, ~r/had no matching bulk strategy that could be used/, fn ->
|
||||
Post
|
||||
|> Ash.Query.for_read(:paginated, authorize?: true)
|
||||
|> Ash.bulk_update!(:requires_initial_data, %{},
|
||||
authorize?: true,
|
||||
allow_stream_with: :full_read,
|
||||
authorize_query?: false,
|
||||
return_errors?: true
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.CalculationTest do
|
||||
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
|
||||
import Ash.Expr
|
||||
|
@ -8,48 +8,48 @@ defmodule AshPostgres.CalculationTest do
|
|||
test "an expression calculation can be filtered on" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
post3 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title3"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title3"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "_"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "_"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "_"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
post2
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
assert [%{c_times_p: 6, title: "match"}] =
|
||||
Post
|
||||
|> Ash.Query.load(:c_times_p)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.filter(&(&1.c_times_p == 6))
|
||||
|
||||
assert [
|
||||
|
@ -57,12 +57,12 @@ defmodule AshPostgres.CalculationTest do
|
|||
] =
|
||||
Post
|
||||
|> Ash.Query.filter(c_times_p == 6)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [] =
|
||||
Post
|
||||
|> Ash.Query.filter(author: [has_posts: true])
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations can refer to to_one path attributes in filters" do
|
||||
|
@ -72,18 +72,18 @@ defmodule AshPostgres.CalculationTest do
|
|||
first_name: "Foo",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{author_first_name_calc: "Foo"}] =
|
||||
Post
|
||||
|> Ash.Query.filter(author_first_name_calc == "Foo")
|
||||
|> Ash.Query.load(:author_first_name_calc)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations can refer to to_one path attributes in sorts" do
|
||||
|
@ -93,44 +93,44 @@ defmodule AshPostgres.CalculationTest do
|
|||
first_name: "Foo",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{author_first_name_calc: "Foo"}] =
|
||||
Post
|
||||
|> Ash.Query.sort(:author_first_name_calc)
|
||||
|> Ash.Query.load(:author_first_name_calc)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations can refer to embedded attributes" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{title: "Mr."} =
|
||||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:title)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "calculations can use the || operator" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{bio: %{title: "Mr.", bio: "Bones"}})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{first_name_or_bob: "bob"} =
|
||||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:first_name_or_bob)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "calculations can use the && operator" do
|
||||
|
@ -140,60 +140,60 @@ defmodule AshPostgres.CalculationTest do
|
|||
first_name: "fred",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{first_name_and_bob: "bob"} =
|
||||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:first_name_and_bob)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "calculations can be used in related filters" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
post3 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title3"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title3"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post2, post3], type: :append_and_remove)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
post2
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [post3], type: :append_and_remove)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
posts_query =
|
||||
Post
|
||||
|
@ -202,7 +202,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
assert %{post: %{c_times_p: 6}} =
|
||||
Comment
|
||||
|> Ash.Query.load(post: posts_query)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.filter(&(&1.post.c_times_p == 6))
|
||||
|> Enum.at(0)
|
||||
|
||||
|
@ -214,20 +214,20 @@ defmodule AshPostgres.CalculationTest do
|
|||
|
||||
assert [
|
||||
%{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
|
||||
|
||||
test "concat calculation can be filtered on" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "not", last_name: "match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "not", last_name: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
author_id = author.id
|
||||
|
||||
|
@ -235,60 +235,60 @@ defmodule AshPostgres.CalculationTest do
|
|||
Author
|
||||
|> Ash.Query.load(:full_name)
|
||||
|> Ash.Query.filter(full_name == "is match")
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "calculations that refer to aggregates in comparison expressions can be filtered on" do
|
||||
Post
|
||||
|> Ash.Query.load(:has_future_comment)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations that refer to aggregates can be authorized" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "comment"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "comment"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{has_future_comment: false} =
|
||||
Post
|
||||
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
||||
|> Ash.Query.for_read(:allow_any, %{})
|
||||
|> Api.read_one!(authorize?: true)
|
||||
|> Ash.read_one!(authorize?: true)
|
||||
|
||||
assert %{has_future_comment: true} =
|
||||
Post
|
||||
|> Ash.Query.load([:has_future_comment, :latest_comment_created_at])
|
||||
|> Ash.Query.for_read(:allow_any, %{})
|
||||
|> Api.read_one!(authorize?: false)
|
||||
|> Ash.read_one!(authorize?: false)
|
||||
|
||||
assert %{has_future_comment: false} =
|
||||
Post
|
||||
|> Ash.Query.for_read(:allow_any, %{})
|
||||
|> Api.read_one!()
|
||||
|> Api.load!([:has_future_comment, :latest_comment_created_at], authorize?: true)
|
||||
|> Ash.read_one!()
|
||||
|> Ash.load!([:has_future_comment, :latest_comment_created_at], authorize?: true)
|
||||
|
||||
assert %{has_future_comment: true} =
|
||||
Post
|
||||
|> Ash.Query.for_read(:allow_any, %{})
|
||||
|> Api.read_one!()
|
||||
|> Api.load!([:has_future_comment, :latest_comment_created_at], authorize?: false)
|
||||
|> Ash.read_one!()
|
||||
|> Ash.load!([:has_future_comment, :latest_comment_created_at], authorize?: false)
|
||||
end
|
||||
|
||||
test "conditional calculations can be filtered on" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "tom"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "tom"})
|
||||
|> Ash.create!()
|
||||
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||
|> Ash.create!()
|
||||
|
||||
author_id = author.id
|
||||
|
||||
|
@ -296,45 +296,45 @@ defmodule AshPostgres.CalculationTest do
|
|||
Author
|
||||
|> Ash.Query.load([:conditional_full_name, :full_name])
|
||||
|> Ash.Query.filter(conditional_full_name == "(none)")
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "parameterized calculations can be filtered on" do
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{param_full_name: "tom holland"} =
|
||||
Author
|
||||
|> Ash.Query.load(:param_full_name)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
|
||||
assert %{param_full_name: "tom~holland"} =
|
||||
Author
|
||||
|> Ash.Query.load(param_full_name: [separator: "~"])
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
|
||||
assert %{} =
|
||||
Author
|
||||
|> Ash.Query.filter(param_full_name(separator: "~") == "tom~holland")
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "parameterized related calculations can be filtered on" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{title: "match"} =
|
||||
Comment
|
||||
|> Ash.Query.filter(author.param_full_name(separator: "~") == "tom~holland")
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
|
||||
assert %{title: "match"} =
|
||||
Comment
|
||||
|
@ -342,93 +342,97 @@ defmodule AshPostgres.CalculationTest do
|
|||
author.param_full_name(separator: "~") == "tom~holland" and
|
||||
author.param_full_name(separator: " ") == "tom holland"
|
||||
)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "parameterized calculations can be sorted on" do
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "tom", last_name: "holland"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "tom", last_name: "holland"})
|
||||
|> Ash.create!()
|
||||
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "abc", last_name: "def"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "abc", last_name: "def"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{first_name: "abc"}, %{first_name: "tom"}] =
|
||||
Author
|
||||
|> Ash.Query.sort(param_full_name: [separator: "~"])
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations using if and literal boolean results can run" do
|
||||
Post
|
||||
|> Ash.Query.load(:was_created_in_the_last_month)
|
||||
|> Ash.Query.filter(was_created_in_the_last_month == true)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "nested conditional calculations can be loaded" do
|
||||
Author
|
||||
|> Ash.Changeset.new(%{last_name: "holland"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{last_name: "holland"})
|
||||
|> Ash.create!()
|
||||
|
||||
Author
|
||||
|> Ash.Changeset.new(%{first_name: "tom"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "tom"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{nested_conditional: "No First Name"}, %{nested_conditional: "No Last Name"}] =
|
||||
Author
|
||||
|> Ash.Query.load(:nested_conditional)
|
||||
|> Ash.Query.sort(:nested_conditional)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations load nullable timestamp aggregates compared to a fragment" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Query.load([:has_future_arbitrary_timestamp])
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "loading a calculation loads its dependent loads" do
|
||||
user =
|
||||
User
|
||||
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
account =
|
||||
Account
|
||||
|> Ash.Changeset.for_create(:create, %{is_active: true})
|
||||
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Api.load!([:active])
|
||||
|> Ash.create!()
|
||||
|> Ash.load!([:active])
|
||||
|
||||
assert account.active
|
||||
end
|
||||
|
@ -442,7 +446,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{
|
||||
full_name_with_nils: "Bill Jones",
|
||||
|
@ -452,7 +456,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:full_name_with_nils)
|
||||
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "with nil value" do
|
||||
|
@ -462,7 +466,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
first_name: "Bill",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{
|
||||
full_name_with_nils: "Bill",
|
||||
|
@ -472,23 +476,23 @@ defmodule AshPostgres.CalculationTest do
|
|||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:full_name_with_nils)
|
||||
|> Ash.Query.load(:full_name_with_nils_no_joiner)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
end
|
||||
|
||||
test "arguments with cast_in_query?: false are not cast" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "not", score: 42})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "not", score: 42})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [post] =
|
||||
Post
|
||||
|> Ash.Query.filter(similarity(search: expr(query(search: "match"))))
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert post.title == "match"
|
||||
end
|
||||
|
@ -502,7 +506,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{
|
||||
split_full_name: ["Bill", "Jones"]
|
||||
|
@ -510,7 +514,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load(:split_full_name)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "trimming whitespace" do
|
||||
|
@ -521,7 +525,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones ",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{
|
||||
split_full_name_trim: ["Bill", "Jones"],
|
||||
|
@ -530,37 +534,37 @@ defmodule AshPostgres.CalculationTest do
|
|||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load([:split_full_name_trim, :split_full_name])
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
end
|
||||
|
||||
describe "count_nils/1" do
|
||||
test "counts nil values" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", nil, "c"]})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{list_containing_nils: ["a", nil, "b", nil, "c"]})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{list_containing_nils: ["a", nil, "b", "c"]})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{list_containing_nils: ["a", nil, "b", "c"]})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [_] =
|
||||
Post
|
||||
|> Ash.Query.filter(count_nils(list_containing_nils) == 2)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
describe "-/1" do
|
||||
test "makes numbers negative" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{negative_score: -42}] =
|
||||
Post
|
||||
|> Ash.Query.load(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -568,39 +572,39 @@ defmodule AshPostgres.CalculationTest do
|
|||
test "maps can reference filtered aggregates" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{agg_map: %{called_foo: 2, called_bar: 1}}] =
|
||||
Post
|
||||
|> Ash.Query.load(:agg_map)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "maps can be constructed" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match", score: 42})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match", score: 42})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{score_map: %{negative_score: %{foo: -42}}}] =
|
||||
Post
|
||||
|> Ash.Query.load(:score_map)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -613,7 +617,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones ",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{
|
||||
first_name_from_split: "Bill"
|
||||
|
@ -621,15 +625,15 @@ defmodule AshPostgres.CalculationTest do
|
|||
Author
|
||||
|> Ash.Query.filter(id == ^author.id)
|
||||
|> Ash.Query.load([:first_name_from_split])
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
end
|
||||
|
||||
test "dependent calc" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match", price: 10_024})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match", price: 10_024})
|
||||
|> Ash.create!()
|
||||
|
||||
Post.get_by_id(post.id,
|
||||
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
|
||||
assert "thing" =
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Api.load!(:foo_bar_from_stuff)
|
||||
|> Ash.create!()
|
||||
|> Ash.load!(:foo_bar_from_stuff)
|
||||
|> Map.get(:foo_bar_from_stuff)
|
||||
end
|
||||
|
||||
|
@ -654,19 +662,19 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %AshPostgres.Test.Money{} =
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Api.load!(:calc_returning_json)
|
||||
|> Ash.create!()
|
||||
|> Ash.load!(:calc_returning_json)
|
||||
|> Map.get(:calc_returning_json)
|
||||
|
||||
assert [%AshPostgres.Test.Money{}] =
|
||||
author
|
||||
|> Api.load!(posts: :calc_returning_json)
|
||||
|> Ash.load!(posts: :calc_returning_json)
|
||||
|> Map.get(:posts)
|
||||
|> Enum.map(&Map.get(&1, :calc_returning_json))
|
||||
end
|
||||
|
@ -678,7 +686,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{calculations: %{length: 9}} =
|
||||
Author
|
||||
|
@ -687,7 +695,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
expr(string_length(string_trim(first_name <> last_name <> " "))),
|
||||
:integer
|
||||
)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "an expression calculation that loads a runtime calculation works" do
|
||||
|
@ -697,12 +705,12 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{expr_referencing_runtime: "Bill Jones Bill Jones"}] =
|
||||
Author
|
||||
|> Ash.Query.load(:expr_referencing_runtime)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "lazy values are evaluated lazily" do
|
||||
|
@ -712,7 +720,7 @@ defmodule AshPostgres.CalculationTest do
|
|||
last_name: "Jones",
|
||||
bio: %{title: "Mr.", bio: "Bones"}
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{calculations: %{string: "fred"}} =
|
||||
Author
|
||||
|
@ -721,21 +729,21 @@ defmodule AshPostgres.CalculationTest do
|
|||
expr(lazy({__MODULE__, :fred, []})),
|
||||
:string
|
||||
)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "exists with a relationship that has a filtered read action works" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
post_id = post.id
|
||||
|
||||
assert [%{id: ^post_id}] =
|
||||
Post
|
||||
|> Ash.Query.filter(has_no_followers)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
def fred do
|
||||
|
|
|
@ -7,29 +7,29 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
certification =
|
||||
AshPostgres.Test.ComplexCalculations.Certification
|
||||
|> Ash.Changeset.new()
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
skill =
|
||||
AshPostgres.Test.ComplexCalculations.Skill
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:certification, certification, type: :append)
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
_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)
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
skill =
|
||||
skill
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([:latest_documentation_status])
|
||||
|> Ash.load!([:latest_documentation_status])
|
||||
|
||||
assert skill.latest_documentation_status == :demonstrated
|
||||
|
||||
certification =
|
||||
certification
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
||||
|> Ash.load!([
|
||||
:count_of_skills
|
||||
])
|
||||
|
||||
|
@ -37,7 +37,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
|
||||
certification =
|
||||
certification
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
||||
|> Ash.load!([
|
||||
:count_of_approved_skills
|
||||
])
|
||||
|
||||
|
@ -45,7 +45,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
|
||||
certification =
|
||||
certification
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
||||
|> Ash.load!([
|
||||
:count_of_documented_skills
|
||||
])
|
||||
|
||||
|
@ -53,7 +53,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
|
||||
certification =
|
||||
certification
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
||||
|> Ash.load!([
|
||||
:count_of_documented_skills,
|
||||
:all_documentation_approved,
|
||||
:some_documentation_created
|
||||
|
@ -66,35 +66,35 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
channel =
|
||||
AshPostgres.Test.ComplexCalculations.Channel
|
||||
|> Ash.Changeset.new()
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user_1 =
|
||||
AshPostgres.Test.User
|
||||
|> Ash.Changeset.for_create(:create, %{name: "User 1"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user_2 =
|
||||
AshPostgres.Test.User
|
||||
|> Ash.Changeset.for_create(:create, %{name: "User 2"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
channel_member_1 =
|
||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:channel, channel, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:user, user_1, type: :append)
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
channel_member_2 =
|
||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:channel, channel, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:user, user_2, type: :append)
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
channel =
|
||||
channel
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
||||
|> Ash.load!([
|
||||
:first_member,
|
||||
:second_member
|
||||
])
|
||||
|
@ -104,13 +104,13 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
|
||||
channel =
|
||||
channel
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!(:name, actor: user_1)
|
||||
|> Ash.load!(:name, actor: user_1)
|
||||
|
||||
assert channel.name == user_1.name
|
||||
|
||||
channel =
|
||||
channel
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!(:name, actor: user_2)
|
||||
|> Ash.load!(:name, actor: user_2)
|
||||
|
||||
assert channel.name == user_2.name
|
||||
end
|
||||
|
@ -119,31 +119,31 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
dm_channel =
|
||||
AshPostgres.Test.ComplexCalculations.DMChannel
|
||||
|> Ash.Changeset.new()
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user_1 =
|
||||
AshPostgres.Test.User
|
||||
|> Ash.Changeset.for_create(:create, %{name: "User 1"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user_2 =
|
||||
AshPostgres.Test.User
|
||||
|> Ash.Changeset.for_create(:create, %{name: "User 2"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
channel_member_1 =
|
||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||
|> 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 =
|
||||
AshPostgres.Test.ComplexCalculations.ChannelMember
|
||||
|> 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
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([
|
||||
|> Ash.load!([
|
||||
:first_member,
|
||||
:second_member
|
||||
])
|
||||
|
@ -153,24 +153,24 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
channels =
|
||||
AshPostgres.Test.ComplexCalculations.Channel
|
||||
|> Ash.Query.for_read(:read)
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
channels =
|
||||
channels
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.load!([dm_channel: :name],
|
||||
|> Ash.load!([dm_channel: :name],
|
||||
actor: user_1
|
||||
)
|
||||
|
||||
|
@ -180,7 +180,7 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
|
||||
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
|
||||
end
|
||||
|
@ -188,92 +188,92 @@ defmodule AshPostgres.Test.ComplexCalculationsTest do
|
|||
test "calculations with parent filters can be filtered on themselves" do
|
||||
AshPostgres.Test.ComplexCalculations.DMChannel
|
||||
|> Ash.Changeset.new()
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{foo: "foobar"}] =
|
||||
AshPostgres.Test.ComplexCalculations.Channel
|
||||
|> Ash.Query.filter(foo == "foobar")
|
||||
|> AshPostgres.Test.ComplexCalculations.Api.read!(load: :foo)
|
||||
|> Ash.read!(load: :foo)
|
||||
end
|
||||
|
||||
test "calculations with aggregates can be referenced from aggregates" do
|
||||
author =
|
||||
AshPostgres.Test.Author
|
||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{author_count_of_posts: 1}] =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Query.load(:author_count_of_posts)
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%{author_count_of_posts: 1}] =
|
||||
AshPostgres.Test.Post
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> AshPostgres.Test.Api.load!(:author_count_of_posts)
|
||||
|> Ash.read!()
|
||||
|> Ash.load!(:author_count_of_posts)
|
||||
|
||||
assert [_] =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Query.filter(author_count_of_posts == 1)
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations can reference aggregates from optimizable first aggregates" do
|
||||
author =
|
||||
AshPostgres.Test.Author
|
||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{author_count_of_posts_agg: 1}] =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Query.load(:author_count_of_posts_agg)
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%{author_count_of_posts_agg: 1}] =
|
||||
AshPostgres.Test.Post
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> AshPostgres.Test.Api.load!(:author_count_of_posts_agg)
|
||||
|> Ash.read!()
|
||||
|> Ash.load!(:author_count_of_posts_agg)
|
||||
|
||||
assert [_] =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Query.filter(author_count_of_posts_agg == 1)
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations can reference aggregates from non optimizable aggregates" do
|
||||
author =
|
||||
AshPostgres.Test.Author
|
||||
|> Ash.Changeset.new(%{first_name: "is", last_name: "match"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "is", last_name: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{sum_of_author_count_of_posts: 1}] =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Query.load(:sum_of_author_count_of_posts)
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%{sum_of_author_count_of_posts: 1}] =
|
||||
AshPostgres.Test.Post
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> AshPostgres.Test.Api.load!(:sum_of_author_count_of_posts)
|
||||
|> Ash.read!()
|
||||
|> Ash.load!(:sum_of_author_count_of_posts)
|
||||
|
||||
assert [_] =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Query.filter(sum_of_author_count_of_posts == 1)
|
||||
|> AshPostgres.Test.Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
defmodule AshPostgres.Test.CompositeTypeTest do
|
||||
use AshPostgres.RepoCase
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
require Ash.Query
|
||||
|
||||
test "can be cast and stored" do
|
||||
post =
|
||||
Post
|
||||
|> 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.y == 2
|
||||
|
@ -17,22 +17,22 @@ defmodule AshPostgres.Test.CompositeTypeTest do
|
|||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
post_id = post.id
|
||||
|
||||
assert %{id: ^post_id} = Post |> Ash.Query.filter(composite_point[:x] == 1) |> Api.read_one!()
|
||||
refute Post |> Ash.Query.filter(composite_point[:x] == 2) |> 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) |> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "composite types can be constructed" do
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "locked", composite_point: %{x: 1, y: 2}})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{composite_origin: %{x: 0, y: 0}} =
|
||||
Post
|
||||
|> Ash.Query.load(:composite_origin)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
defmodule AshPostgres.ConstraintTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Query
|
||||
|
||||
test "constraint messages are properly raised" do
|
||||
assert_raise Ash.Error.Invalid, ~r/yo, bad price/, fn ->
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title", price: -1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title", price: -1})
|
||||
|> Ash.create!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
defmodule AshPostgres.Test.CustomIndexTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Query
|
||||
|
||||
test "unique constraint errors are properly caught" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "first", uniq_custom_one: "what", uniq_custom_two: "what2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
title: "first",
|
||||
uniq_custom_one: "what",
|
||||
uniq_custom_two: "what2"
|
||||
})
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Invalid,
|
||||
~r/Invalid value provided for uniq_custom_one: dude what the heck/,
|
||||
fn ->
|
||||
Post
|
||||
|> Ash.Changeset.new(%{
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
title: "first",
|
||||
uniq_custom_one: "what",
|
||||
uniq_custom_two: "what2"
|
||||
})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
defmodule AshPostgres.DistinctTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Query
|
||||
|
||||
setup do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "foo", score: 2})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", score: 2})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "foo", score: 2})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "foo", score: 2})
|
||||
|> Ash.create!()
|
||||
|
||||
:ok
|
||||
end
|
||||
|
@ -30,7 +30,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
Post
|
||||
|> Ash.Query.distinct(:title)
|
||||
|> Ash.Query.sort(:title)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%{title: "foo"}, %{title: "title"}] = results
|
||||
end
|
||||
|
@ -40,7 +40,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
Post
|
||||
|> Ash.Query.distinct(:title)
|
||||
|> Ash.Query.sort(title: :desc)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%{title: "title"}, %{title: "foo"}] = results
|
||||
end
|
||||
|
@ -51,7 +51,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct(:title)
|
||||
|> Ash.Query.sort(id: :desc)
|
||||
|> Ash.Query.limit(3)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [_, _] = results
|
||||
end
|
||||
|
@ -62,7 +62,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct(:title)
|
||||
|> Ash.Query.sort(id: :desc)
|
||||
|> Ash.Query.limit(3)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [_, _] = results
|
||||
end
|
||||
|
@ -73,7 +73,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct(:title)
|
||||
|> Ash.Query.sort(id: :desc)
|
||||
|> Ash.Query.limit(1)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [_] = results
|
||||
end
|
||||
|
@ -84,7 +84,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct(:negative_score)
|
||||
|> Ash.Query.sort(:negative_score)
|
||||
|> Ash.Query.load(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [
|
||||
%{title: "foo", negative_score: -2},
|
||||
|
@ -96,7 +96,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct(:negative_score)
|
||||
|> Ash.Query.sort(negative_score: :desc)
|
||||
|> Ash.Query.load(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [
|
||||
%{title: "title", negative_score: -1},
|
||||
|
@ -108,7 +108,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct(:negative_score)
|
||||
|> Ash.Query.sort(:title)
|
||||
|> Ash.Query.load(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [
|
||||
%{title: "foo", negative_score: -2},
|
||||
|
@ -118,29 +118,29 @@ defmodule AshPostgres.DistinctTest do
|
|||
|
||||
test "distinct, join filters and sort can be combined" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "a", score: 2})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "a", score: 2})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "a", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "a", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [] =
|
||||
Post
|
||||
|> Ash.Query.distinct(:negative_score)
|
||||
|> Ash.Query.filter(author.first_name == "a")
|
||||
|> Ash.Query.sort(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "distinct sort is applied" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "a", score: 2})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "a", score: 2})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "a", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "a", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
results =
|
||||
Post
|
||||
|
@ -148,7 +148,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct_sort(:title)
|
||||
|> Ash.Query.sort(:negative_score)
|
||||
|> Ash.Query.load(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [
|
||||
%{title: "a", negative_score: -2},
|
||||
|
@ -161,7 +161,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
|> Ash.Query.distinct_sort(title: :desc)
|
||||
|> Ash.Query.sort(:negative_score)
|
||||
|> Ash.Query.load(:negative_score)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [
|
||||
%{title: "foo", negative_score: -2},
|
||||
|
@ -173,7 +173,7 @@ defmodule AshPostgres.DistinctTest do
|
|||
results =
|
||||
Post
|
||||
|> Ash.Query.distinct(:title)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [_, _] = results
|
||||
end
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
defmodule AshPostgres.EmbeddableResourceTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Author, Bio, Post}
|
||||
alias AshPostgres.Test.{Author, Bio, Post}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
setup do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
%{post: post}
|
||||
end
|
||||
|
||||
test "calculations can load json", %{post: post} do
|
||||
assert %{calc_returning_json: %AshPostgres.Test.Money{amount: 100, currency: :usd}} =
|
||||
Api.load!(post, :calc_returning_json)
|
||||
Ash.load!(post, :calc_returning_json)
|
||||
end
|
||||
|
||||
test "embeds with list attributes set to nil are loaded as nil" do
|
||||
post =
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.new(%{bio: %Bio{list_of_strings: nil}})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{bio: %Bio{list_of_strings: nil}})
|
||||
|> 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
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
defmodule AshPostgres.EnumTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Query
|
||||
|
||||
test "valid values are properly inserted" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title", status: :open})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title", status: :open})
|
||||
|> Ash.create!()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
defmodule AshPostgres.ErrorExprTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Query
|
||||
import Ash.Expr
|
||||
|
||||
test "exceptions in filters are treated as regular Ash exceptions" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
||||
Post
|
||||
|> Ash.Query.filter(
|
||||
error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)
|
||||
)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
test "exceptions in calculations are treated as regular Ash exceptions" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Invalid, ~r/this is bad!/, fn ->
|
||||
Post
|
||||
|
@ -31,15 +31,15 @@ defmodule AshPostgres.ErrorExprTest do
|
|||
expr(error(Ash.Error.Query.InvalidFilterValue, message: "this is bad!", value: 10)),
|
||||
:string
|
||||
)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.map(& &1.calculations)
|
||||
end
|
||||
end
|
||||
|
||||
test "exceptions in calculations are treated as regular Ash exceptions in transactions" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Invalid, ~r/this is bad!/, 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)),
|
||||
:string
|
||||
)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|> Enum.map(& &1.calculations)
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule FilterFieldPolicyTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
|
||||
alias AshPostgres.Test.{Api, Organization, Post, User}
|
||||
alias AshPostgres.Test.{Organization, Post, User}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
@ -9,15 +9,15 @@ defmodule FilterFieldPolicyTest do
|
|||
organization =
|
||||
Organization
|
||||
|> Ash.Changeset.for_create(:create, %{name: "test_org"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
User
|
||||
|> Ash.Changeset.for_create(:create, %{organization_id: organization.id, name: "foo bar"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{organization_id: organization.id})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
filter = Ash.Filter.parse_input!(Post, %{organization: %{name: %{ilike: "%org"}}})
|
||||
|
||||
|
@ -25,7 +25,7 @@ defmodule FilterFieldPolicyTest do
|
|||
Post
|
||||
|> Ash.Query.do_filter(filter)
|
||||
|> 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"}}}})
|
||||
|
||||
|
@ -33,6 +33,6 @@ defmodule FilterFieldPolicyTest do
|
|||
Post
|
||||
|> Ash.Query.do_filter(filter)
|
||||
|> Ash.Query.for_read(:allow_any)
|
||||
|> Api.read!(actor: %{id: "test"})
|
||||
|> Ash.read!(actor: %{id: "test"})
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.Test.LoadTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Comment, Post, Record, TempEntity}
|
||||
alias AshPostgres.Test.{Comment, Post, Record, TempEntity}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
@ -8,18 +8,18 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
results =
|
||||
Post
|
||||
|> Ash.Query.load(:comments)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%Post{comments: [%{title: "match"}]}] = results
|
||||
end
|
||||
|
@ -28,18 +28,18 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
assert %Comment{post: %Ash.NotLoaded{type: :relationship}} =
|
||||
comment =
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.Changeset.manage_relationship(:comments, [comment], type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
results =
|
||||
Comment
|
||||
|> Ash.Query.load(:post)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%Comment{post: %{title: "match"}}] = results
|
||||
end
|
||||
|
@ -47,29 +47,29 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
test "many_to_many loads work" do
|
||||
source_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "source"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "destination"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "destination"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|
||||
|> Ash.create!()
|
||||
|
||||
source_post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||
type: :append_and_remove
|
||||
)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
results =
|
||||
source_post
|
||||
|> Api.load!(:linked_posts)
|
||||
|> Ash.load!(:linked_posts)
|
||||
|
||||
assert %{linked_posts: [%{title: "destination"}, %{title: "destination"}]} = results
|
||||
end
|
||||
|
@ -77,29 +77,29 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
test "many_to_many loads work when nested" do
|
||||
source_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "source"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "destination"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "destination"})
|
||||
|> Ash.create!()
|
||||
|
||||
source_post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post],
|
||||
type: :append_and_remove
|
||||
)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
destination_post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [source_post], type: :append_and_remove)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
results =
|
||||
source_post
|
||||
|> Api.load!(linked_posts: :linked_posts)
|
||||
|> Ash.load!(linked_posts: :linked_posts)
|
||||
|
||||
assert %{linked_posts: [%{title: "destination", linked_posts: [%{title: "source"}]}]} =
|
||||
results
|
||||
|
@ -109,75 +109,75 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
test "parent references are resolved" do
|
||||
post1 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
post2_id = post2.id
|
||||
|
||||
post3 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "no match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{posts_with_matching_title: [%{id: ^post2_id}]}] =
|
||||
Post
|
||||
|> Ash.Query.load(:posts_with_matching_title)
|
||||
|> Ash.Query.filter(id == ^post1.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%{posts_with_matching_title: []}] =
|
||||
Post
|
||||
|> Ash.Query.load(:posts_with_matching_title)
|
||||
|> Ash.Query.filter(id == ^post3.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "parent references work when joining for filters" do
|
||||
%{id: post1_id} =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "no match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "no match"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "no match"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{id: ^post1_id}] =
|
||||
Post
|
||||
|> Ash.Query.filter(posts_with_matching_title.id == ^post2.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "lateral join loads (loads with limits or offsets) are supported" do
|
||||
assert %Post{comments: %Ash.NotLoaded{type: :relationship}} =
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "abc"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "def"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
comments_query =
|
||||
Comment
|
||||
|
@ -187,7 +187,7 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
results =
|
||||
Post
|
||||
|> Ash.Query.load(comments: comments_query)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%Post{comments: [%{title: "abc"}]}] = results
|
||||
|
||||
|
@ -199,7 +199,7 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
results =
|
||||
Post
|
||||
|> Ash.Query.load(comments: comments_query)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%Post{comments: [%{title: "def"}]}] = results
|
||||
|
||||
|
@ -211,7 +211,7 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
results =
|
||||
Post
|
||||
|> Ash.Query.load(comments: comments_query)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [%Post{comments: [%{title: "def"}, %{title: "abc"}]}] = results
|
||||
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
|
||||
source_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "source"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "abc"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "def"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||
|> Ash.create!()
|
||||
|
||||
source_post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||
type: :append_and_remove
|
||||
)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
assert %{linked_posts: [_, _]} = Post.get_by_id!(source_post.id, load: [:linked_posts])
|
||||
end
|
||||
|
@ -245,25 +245,25 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
test "lateral join loads with many to many relationships are supported" do
|
||||
source_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "source"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "abc"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "def"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||
|> Ash.create!()
|
||||
|
||||
source_post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||
type: :append_and_remove
|
||||
)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
linked_posts_query =
|
||||
Post
|
||||
|
@ -272,7 +272,7 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
|
||||
results =
|
||||
source_post
|
||||
|> Api.load!(linked_posts: linked_posts_query)
|
||||
|> Ash.load!(linked_posts: linked_posts_query)
|
||||
|
||||
assert %{linked_posts: [%{title: "abc"}]} = results
|
||||
|
||||
|
@ -283,7 +283,7 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
|
||||
results =
|
||||
source_post
|
||||
|> Api.load!(linked_posts: linked_posts_query)
|
||||
|> Ash.load!(linked_posts: linked_posts_query)
|
||||
|
||||
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
|
||||
end
|
||||
|
@ -291,25 +291,25 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
test "lateral join loads with many to many relationships are supported with aggregates" do
|
||||
source_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "source"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "source"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "abc"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "abc"})
|
||||
|> Ash.create!()
|
||||
|
||||
destination_post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "def"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "def"})
|
||||
|> Ash.create!()
|
||||
|
||||
source_post
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:linked_posts, [destination_post, destination_post2],
|
||||
type: :append_and_remove
|
||||
)
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
linked_posts_query =
|
||||
Post
|
||||
|
@ -318,7 +318,7 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
|
||||
results =
|
||||
source_post
|
||||
|> Api.load!(linked_posts: linked_posts_query)
|
||||
|> Ash.load!(linked_posts: linked_posts_query)
|
||||
|
||||
assert %{linked_posts: [%{title: "abc"}]} = results
|
||||
|
||||
|
@ -330,16 +330,18 @@ defmodule AshPostgres.Test.LoadTest do
|
|||
|
||||
results =
|
||||
source_post
|
||||
|> Api.load!(linked_posts: linked_posts_query)
|
||||
|> Ash.load!(linked_posts: linked_posts_query)
|
||||
|
||||
assert %{linked_posts: [%{title: "abc"}, %{title: "def"}]} = results
|
||||
end
|
||||
|
||||
test "lateral join loads with read action from a custom table and schema" do
|
||||
record = Record |> Ash.Changeset.new(%{full_name: "name"}) |> Api.create!()
|
||||
temp_entity = TempEntity |> Ash.Changeset.new(%{full_name: "name"}) |> Api.create!()
|
||||
record = Record |> Ash.Changeset.for_create(:create, %{full_name: "name"}) |> Ash.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
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.Test.LockTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
require Ash.Query
|
||||
|
||||
setup do
|
||||
|
@ -17,7 +17,7 @@ defmodule AshPostgres.Test.LockTest do
|
|||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{title: "locked"})
|
||||
|> Ash.Changeset.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
task1 =
|
||||
Task.async(fn ->
|
||||
|
@ -26,7 +26,7 @@ defmodule AshPostgres.Test.LockTest do
|
|||
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|
||||
|> Ash.Query.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
||||
|> Ash.Query.filter(id == ^post.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
:timer.sleep(1000)
|
||||
:ok
|
||||
|
@ -43,7 +43,7 @@ defmodule AshPostgres.Test.LockTest do
|
|||
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|
||||
|> Ash.Query.set_context(%{data_layer: %{repo: AshPostgres.TestNoSandboxRepo}})
|
||||
|> Ash.Query.filter(id == ^post.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.Test.ManualRelationshipsTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Comment, Post}
|
||||
alias AshPostgres.Test.{Comment, Post}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
@ -8,102 +8,102 @@ defmodule AshPostgres.Test.ManualRelationshipsTest do
|
|||
test "aggregates can be loaded with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{count_of_comments_containing_title: 0} =
|
||||
Api.load!(post, :count_of_comments_containing_title)
|
||||
Ash.load!(post, :count_of_comments_containing_title)
|
||||
end
|
||||
|
||||
test "aggregates can be loaded with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{count_of_comments_containing_title: 1} =
|
||||
Api.load!(post, :count_of_comments_containing_title)
|
||||
Ash.load!(post, :count_of_comments_containing_title)
|
||||
end
|
||||
|
||||
test "relationships can be filtered on with no data" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [] =
|
||||
Post |> Ash.Query.filter(comments_containing_title.title == "title") |> Api.read!()
|
||||
Post |> Ash.Query.filter(comments_containing_title.title == "title") |> Ash.read!()
|
||||
end
|
||||
|
||||
test "aggregates can be filtered on with no data" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> 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
|
||||
|
||||
test "aggregates can be filtered on with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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
|
||||
|
||||
test "relationships can be filtered on with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [_] =
|
||||
Post
|
||||
|> Ash.Query.filter(comments_containing_title.title == "title2")
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -111,128 +111,128 @@ defmodule AshPostgres.Test.ManualRelationshipsTest do
|
|||
test "aggregates can be loaded with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> 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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{count_of_comments_containing_title: 0} =
|
||||
Api.load!(comment, :count_of_comments_containing_title)
|
||||
Ash.load!(comment, :count_of_comments_containing_title)
|
||||
end
|
||||
|
||||
test "aggregates can be loaded with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> 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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{count_of_comments_containing_title: 1} =
|
||||
Api.load!(comment, :count_of_comments_containing_title)
|
||||
Ash.load!(comment, :count_of_comments_containing_title)
|
||||
end
|
||||
|
||||
test "aggregates can be filtered on with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [] =
|
||||
Comment
|
||||
|> Ash.Query.filter(count_of_comments_containing_title == 1)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "relationships can be filtered on with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [] =
|
||||
Comment
|
||||
|> Ash.Query.filter(post.comments_containing_title.title == "title2")
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "aggregates can be filtered on with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [_, _] =
|
||||
Comment
|
||||
|> Ash.Query.filter(count_of_comments_containing_title == 1)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "relationships can be filtered on with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [_, _] =
|
||||
Comment
|
||||
|> Ash.Query.filter(post.comments_containing_title.title == "title2")
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -240,128 +240,128 @@ defmodule AshPostgres.Test.ManualRelationshipsTest do
|
|||
test "aggregates can be loaded with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> 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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{posts_for_comments_containing_title: []} =
|
||||
Api.load!(comment, :posts_for_comments_containing_title)
|
||||
Ash.load!(comment, :posts_for_comments_containing_title)
|
||||
end
|
||||
|
||||
test "aggregates can be loaded with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> 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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{posts_for_comments_containing_title: ["title"]} =
|
||||
Api.load!(comment, :posts_for_comments_containing_title)
|
||||
Ash.load!(comment, :posts_for_comments_containing_title)
|
||||
end
|
||||
|
||||
test "aggregates can be filtered on with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [] =
|
||||
Comment
|
||||
|> Ash.Query.filter("title" in posts_for_comments_containing_title)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "relationships can be filtered on with no data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [] =
|
||||
Comment
|
||||
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "aggregates can be filtered on with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [_, _] =
|
||||
Comment
|
||||
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "relationships can be filtered on with data" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.create!()
|
||||
|
||||
Comment
|
||||
|> Ash.Changeset.new(%{title: "title2"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title2"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [_, _] =
|
||||
Comment
|
||||
|> Ash.Query.filter(post.comments_containing_title.post.title == "title")
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,18 +4,18 @@ defmodule AshPostgres.ManualUpdateTest do
|
|||
test "Manual update defined in a module to update an attribute" do
|
||||
post =
|
||||
AshPostgres.Test.Post
|
||||
|> Ash.Changeset.new(%{title: "match"})
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "match"})
|
||||
|> Ash.create!()
|
||||
|
||||
AshPostgres.Test.Comment
|
||||
|> Ash.Changeset.new(%{title: "_"})
|
||||
|> Ash.Changeset.for_create(:create, %{title: "_"})
|
||||
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
||||
|> AshPostgres.Test.Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
post =
|
||||
post
|
||||
|> Ash.Changeset.for_update(:manual_update)
|
||||
|> AshPostgres.Test.Api.update!()
|
||||
|> Ash.update!()
|
||||
|
||||
assert post.title == "manual"
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,18 @@
|
|||
defmodule AshPostgres.Test.MultitenancyTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
|
||||
alias AshPostgres.MultitenancyTest.{Api, Org, Post, User}
|
||||
alias AshPostgres.MultitenancyTest.{Org, Post, User}
|
||||
|
||||
setup do
|
||||
org1 =
|
||||
Org
|
||||
|> Ash.Changeset.new(name: "test1")
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{name: "test1"})
|
||||
|> Ash.create!()
|
||||
|
||||
org2 =
|
||||
Org
|
||||
|> Ash.Changeset.new(name: "test2")
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{name: "test2"})
|
||||
|> Ash.create!()
|
||||
|
||||
[org1: org1, org2: org2]
|
||||
end
|
||||
|
@ -34,17 +34,17 @@ defmodule AshPostgres.Test.MultitenancyTest do
|
|||
assert [%{id: ^org_id}] =
|
||||
Org
|
||||
|> Ash.Query.set_tenant(tenant(org1))
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "context multitenancy works with policies", %{org1: org1} do
|
||||
Post
|
||||
|> Ash.Changeset.new(name: "foo")
|
||||
|> Ash.Changeset.for_create(:create, %{name: "foo"})
|
||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|> Ash.Changeset.for_update(:update_with_policy, %{}, authorize?: true)
|
||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||
|> Api.update!()
|
||||
|> Ash.update!()
|
||||
end
|
||||
|
||||
test "attribute multitenancy is set on creation" do
|
||||
|
@ -52,29 +52,29 @@ defmodule AshPostgres.Test.MultitenancyTest do
|
|||
|
||||
org =
|
||||
Org
|
||||
|> Ash.Changeset.new(name: "test3")
|
||||
|> Ash.Changeset.for_create(:create, %{name: "test3"})
|
||||
|> Ash.Changeset.set_tenant("org_#{uuid}")
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert org.id == uuid
|
||||
end
|
||||
|
||||
test "schema multitenancy works", %{org1: org1, org2: org2} do
|
||||
Post
|
||||
|> Ash.Changeset.new(name: "foo")
|
||||
|> Ash.Changeset.for_create(:create, %{name: "foo"})
|
||||
|> 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(org2)) |> Api.read!()
|
||||
assert [_] = Post |> Ash.Query.set_tenant(tenant(org1)) |> Ash.read!()
|
||||
assert [] = Post |> Ash.Query.set_tenant(tenant(org2)) |> Ash.read!()
|
||||
end
|
||||
|
||||
test "schema rename on update works", %{org1: org1} do
|
||||
new_uuid = Ash.UUID.generate()
|
||||
|
||||
org1
|
||||
|> Ash.Changeset.new(id: new_uuid)
|
||||
|> Api.update!()
|
||||
|> Ash.Changeset.for_update(:update, %{id: new_uuid})
|
||||
|> Ash.update!()
|
||||
|
||||
new_tenant = "org_#{new_uuid}"
|
||||
|
||||
|
@ -91,106 +91,106 @@ defmodule AshPostgres.Test.MultitenancyTest do
|
|||
org =
|
||||
Org
|
||||
|> Ash.Changeset.new()
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user =
|
||||
User
|
||||
|> Ash.Changeset.new()
|
||||
|> 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
|
||||
|
||||
test "loading context multitenant resources from attribute multitenant resources works" do
|
||||
org =
|
||||
Org
|
||||
|> Ash.Changeset.new()
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user1 =
|
||||
User
|
||||
|> Ash.Changeset.new(%{name: "a"})
|
||||
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user2 =
|
||||
User
|
||||
|> Ash.Changeset.new(%{name: "b"})
|
||||
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user1_id = user1.id
|
||||
user2_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
|
||||
|
||||
test "manage_relationship from context multitenant resource to attribute multitenant resource doesn't raise an error" do
|
||||
org = Org |> Ash.Changeset.new() |> Api.create!()
|
||||
user = User |> Ash.Changeset.new() |> Api.create!()
|
||||
org = Org |> Ash.Changeset.new() |> Ash.create!()
|
||||
user = User |> Ash.Changeset.new() |> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, %{}, tenant: tenant(org))
|
||||
|> Ash.Changeset.manage_relationship(:user, user, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
end
|
||||
|
||||
test "loading attribute multitenant resources with limits from context multitenant resources works" do
|
||||
org =
|
||||
Org
|
||||
|> Ash.Changeset.new()
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user =
|
||||
User
|
||||
|> Ash.Changeset.new()
|
||||
|> 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
|
||||
|
||||
test "loading context multitenant resources with limits from attribute multitenant resources works" do
|
||||
org =
|
||||
Org
|
||||
|> Ash.Changeset.new()
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user1 =
|
||||
User
|
||||
|> Ash.Changeset.new(%{name: "a"})
|
||||
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user2 =
|
||||
User
|
||||
|> Ash.Changeset.new(%{name: "b"})
|
||||
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
||||
|> Ash.Changeset.manage_relationship(:org, org, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
user1_id = user1.id
|
||||
user2_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
|
||||
|
||||
test "unique constraints are properly scoped", %{org1: org1} do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{})
|
||||
|> Ash.Changeset.for_create(:create, %{})
|
||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert_raise Ash.Error.Invalid,
|
||||
~r/Invalid value provided for id: has already been taken/,
|
||||
fn ->
|
||||
Post
|
||||
|> Ash.Changeset.new(%{id: post.id})
|
||||
|> Ash.Changeset.for_create(:create, %{id: post.id})
|
||||
|> Ash.Changeset.set_tenant(tenant(org1))
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
defmodule AshPostgres.PolymorphismTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post, Rating}
|
||||
alias AshPostgres.Test.{Post, Rating}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
test "you can create related data" do
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, rating: %{score: 10})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{score: 10}] =
|
||||
Rating
|
||||
|> Ash.Query.set_context(%{data_layer: %{table: "post_ratings"}})
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "you can read related data" do
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create, rating: %{score: 10})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{score: 10}] =
|
||||
Post
|
||||
|> Ash.Query.load(:ratings)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
|> Map.get(:ratings)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
defmodule AshPostgres.Test.PrimaryKeyTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, IntegerPost, Post, PostView}
|
||||
alias AshPostgres.Test.{IntegerPost, Post, PostView}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
describe "resources without a primary key" do
|
||||
|
@ -18,12 +19,12 @@ defmodule AshPostgres.Test.PrimaryKeyTest do
|
|||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_action(:create, %{title: "not very interesting"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert {:ok, view} =
|
||||
PostView
|
||||
|> Ash.Changeset.for_action(:create, %{browser: :firefox, post_id: post.id})
|
||||
|> Api.create()
|
||||
|> Ash.create()
|
||||
|
||||
assert view.browser == :firefox
|
||||
assert view.post_id == post.id
|
||||
|
@ -34,14 +35,14 @@ defmodule AshPostgres.Test.PrimaryKeyTest do
|
|||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_action(:create, %{title: "not very interesting"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
expected =
|
||||
PostView
|
||||
|> 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.browser == expected.browser
|
||||
|
|
|
@ -8,11 +8,12 @@ defmodule AshPostgres.ReferencesTest do
|
|||
defmodule Org do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: nil,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:name, :string)
|
||||
attribute(:name, :string, public?: true)
|
||||
end
|
||||
|
||||
multitenancy do
|
||||
|
@ -33,14 +34,15 @@ defmodule AshPostgres.ReferencesTest do
|
|||
defmodule User do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: nil,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:secondary_id, :uuid)
|
||||
attribute(:foo_id, :uuid)
|
||||
attribute(:name, :string)
|
||||
attribute(:org_id, :uuid)
|
||||
attribute(:secondary_id, :uuid, public?: true)
|
||||
attribute(:foo_id, :uuid, public?: true)
|
||||
attribute(:name, :string, public?: true)
|
||||
attribute(:org_id, :uuid, public?: true)
|
||||
end
|
||||
|
||||
multitenancy do
|
||||
|
@ -49,7 +51,9 @@ defmodule AshPostgres.ReferencesTest do
|
|||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:org, Org)
|
||||
belongs_to(:org, Org) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
|
||||
postgres do
|
||||
|
@ -66,13 +70,14 @@ defmodule AshPostgres.ReferencesTest do
|
|||
defmodule UserThing do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: nil,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
attributes do
|
||||
attribute(:id, :string, primary_key?: true, allow_nil?: false)
|
||||
attribute(:name, :string)
|
||||
attribute(:org_id, :uuid)
|
||||
attribute(:foo_id, :uuid)
|
||||
attribute(:id, :string, primary_key?: true, allow_nil?: false, public?: true)
|
||||
attribute(:name, :string, public?: true)
|
||||
attribute(:org_id, :uuid, public?: true)
|
||||
attribute(:foo_id, :uuid, public?: true)
|
||||
end
|
||||
|
||||
multitenancy do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule AshPostgres.RelWithParentFilterTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
|
||||
alias AshPostgres.Test.{Api, Author}
|
||||
alias AshPostgres.Test.Author
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
@ -9,11 +9,11 @@ defmodule AshPostgres.RelWithParentFilterTest do
|
|||
%{id: author_id} =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "John", last_name: "Doe"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Author
|
||||
|> 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
|
||||
assert %{num_of_authors_with_same_first_name: 1} =
|
||||
|
@ -21,18 +21,18 @@ defmodule AshPostgres.RelWithParentFilterTest do
|
|||
|> Ash.Query.for_read(:read)
|
||||
|> Ash.Query.filter(id == ^author_id)
|
||||
|> Ash.Query.load(:num_of_authors_with_same_first_name)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
end
|
||||
|
||||
test "filter on relationship using parent works as expected when loading relationship" do
|
||||
%{id: author_id} =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "John", last_name: "Doe"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{first_name: "John"})
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert %{authors_with_same_first_name: authors} =
|
||||
Author
|
||||
|
@ -43,7 +43,7 @@ defmodule AshPostgres.RelWithParentFilterTest do
|
|||
# but when doing that it does a inner lateral join
|
||||
# instead of using the id from the parent relationship
|
||||
|> Ash.Query.load(:authors_with_same_first_name)
|
||||
|> Api.read_one!()
|
||||
|> Ash.read_one!()
|
||||
|
||||
assert length(authors) == 1
|
||||
end
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
defmodule AshPostgres.SchemaTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Author, Profile}
|
||||
alias AshPostgres.Test.{Author, Profile}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
setup do
|
||||
[author: Api.create!(Ash.Changeset.for_create(Author, :create, %{}))]
|
||||
[author: Ash.create!(Ash.Changeset.for_create(Author, :create, %{}))]
|
||||
end
|
||||
|
||||
test "data can be created", %{author: author} do
|
||||
|
@ -14,16 +14,16 @@ defmodule AshPostgres.SchemaTest do
|
|||
Profile
|
||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
end
|
||||
|
||||
test "data can be read", %{author: author} do
|
||||
Profile
|
||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||
|> 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
|
||||
|
||||
test "they can be filtered across", %{author: author} do
|
||||
|
@ -31,33 +31,33 @@ defmodule AshPostgres.SchemaTest do
|
|||
Profile
|
||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||
|> 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 [_] =
|
||||
Author
|
||||
|> Ash.Query.filter(profile.id == ^profile.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [_] =
|
||||
Profile
|
||||
|> Ash.Query.filter(author.id == ^author.id)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "aggregates work across schemas", %{author: author} do
|
||||
Profile
|
||||
|> Ash.Changeset.for_create(:create, %{description: "foo"})
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [%{profile_description: "foo"}] =
|
||||
Author
|
||||
|> Ash.Query.filter(profile_description == "foo")
|
||||
|> 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
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
defmodule AshPostgres.SelectTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Post}
|
||||
alias AshPostgres.Test.Post
|
||||
|
||||
require Ash.Query
|
||||
|
||||
test "values not selected in the query are not present in the response" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "title"})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "title"})
|
||||
|> 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
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
defmodule AshPostgres.SortTest do
|
||||
@moduledoc false
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Comment, Post, PostLink}
|
||||
alias AshPostgres.Test.{Comment, Post, PostLink}
|
||||
|
||||
require Ash.Query
|
||||
require Ash.Sort
|
||||
|
||||
test "multi-column sorts work" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [
|
||||
%{title: "aaa", score: 0},
|
||||
%{title: "aaa", score: 1},
|
||||
%{title: "bbb"}
|
||||
] =
|
||||
Api.read!(
|
||||
Ash.read!(
|
||||
Post
|
||||
|> Ash.Query.load(:count_of_comments)
|
||||
|> Ash.Query.sort(title: :asc, score: :asc)
|
||||
|
@ -34,31 +34,31 @@ defmodule AshPostgres.SortTest do
|
|||
test "multi-column sorts work on inclusion" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
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)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
posts =
|
||||
Post
|
||||
|
@ -71,7 +71,7 @@ defmodule AshPostgres.SortTest do
|
|||
|> Ash.Query.limit(1)
|
||||
])
|
||||
|> Ash.Query.sort([:title, :score])
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
|
||||
assert [
|
||||
%{title: "aaa", comments: [%{title: "aaa"}]},
|
||||
|
@ -82,23 +82,23 @@ defmodule AshPostgres.SortTest do
|
|||
|
||||
test "multicolumn sort works with a select statement" do
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "bbb", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
assert [
|
||||
%{title: "aaa", score: 0},
|
||||
%{title: "aaa", score: 1},
|
||||
%{title: "bbb"}
|
||||
] =
|
||||
Api.read!(
|
||||
Ash.read!(
|
||||
Post
|
||||
|> Ash.Query.sort(title: :asc, score: :asc)
|
||||
|> 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
|
||||
post1 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "bbb", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
post3 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "ccc", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "ccc", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
PostLink
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:source_post, post1, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
PostLink
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:source_post, post2, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
PostLink
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:source_post, post3, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
assert [
|
||||
%{title: "aaa"},
|
||||
%{title: "bbb"},
|
||||
%{title: "ccc"}
|
||||
] =
|
||||
Api.read!(
|
||||
Ash.read!(
|
||||
Post
|
||||
|> Ash.Query.sort(title: :asc)
|
||||
|> Ash.Query.filter(linked_posts.title in ["aaa", "bbb", "ccc"])
|
||||
|
@ -155,7 +155,7 @@ defmodule AshPostgres.SortTest do
|
|||
%{title: "bbb"},
|
||||
%{title: "aaa"}
|
||||
] =
|
||||
Api.read!(
|
||||
Ash.read!(
|
||||
Post
|
||||
|> Ash.Query.sort(title: :desc)
|
||||
|> Ash.Query.filter(linked_posts.title in ["aaa", "bbb", "ccc"] or title == "aaa")
|
||||
|
@ -166,7 +166,7 @@ defmodule AshPostgres.SortTest do
|
|||
%{title: "bbb"},
|
||||
%{title: "aaa"}
|
||||
] =
|
||||
Api.read!(
|
||||
Ash.read!(
|
||||
Post
|
||||
|> Ash.Query.sort(title: :desc)
|
||||
|> Ash.Query.filter(
|
||||
|
@ -180,48 +180,48 @@ defmodule AshPostgres.SortTest do
|
|||
Post
|
||||
|> Ash.Query.load(:count_of_comments)
|
||||
|> Ash.Query.sort(:c_times_p)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
test "calculations can sort on expressions" do
|
||||
post1 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "aaa", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "aaa", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
post2 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "bbb", score: 1})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "bbb", score: 1})
|
||||
|> Ash.create!()
|
||||
|
||||
post3 =
|
||||
Post
|
||||
|> Ash.Changeset.new(%{title: "ccc", score: 0})
|
||||
|> Api.create!()
|
||||
|> Ash.Changeset.for_create(:create, %{title: "ccc", score: 0})
|
||||
|> Ash.create!()
|
||||
|
||||
PostLink
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:source_post, post1, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
PostLink
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:source_post, post2, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
PostLink
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.manage_relationship(:source_post, post3, type: :append)
|
||||
|> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append)
|
||||
|> Api.create!()
|
||||
|> Ash.create!()
|
||||
|
||||
posts_query =
|
||||
Ash.Query.sort(Post, Ash.Sort.expr_sort(source(post_links.state)))
|
||||
|
||||
Post
|
||||
|> Ash.Query.load(linked_posts: posts_query)
|
||||
|> Api.read!()
|
||||
|> Ash.read!()
|
||||
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
|
||||
@moduledoc false
|
||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -43,6 +46,8 @@ defmodule AshPostgres.Test.ComplexCalculations.Certification do
|
|||
end
|
||||
|
||||
relationships do
|
||||
has_many(:skills, AshPostgres.Test.ComplexCalculations.Skill)
|
||||
has_many(:skills, AshPostgres.Test.ComplexCalculations.Skill) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
defmodule AshPostgres.Test.ComplexCalculations.Channel do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [Ash.Policy.Authorizer]
|
||||
|
||||
require Ash.Expr
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
|
||||
create_timestamp(:created_at, private?: false)
|
||||
update_timestamp(:updated_at, private?: false)
|
||||
create_timestamp(:created_at, public?: true)
|
||||
update_timestamp(:updated_at, public?: true)
|
||||
end
|
||||
|
||||
postgres do
|
||||
|
@ -23,30 +25,36 @@ defmodule AshPostgres.Test.ComplexCalculations.Channel do
|
|||
end
|
||||
|
||||
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
|
||||
public?(true)
|
||||
destination_attribute(:channel_id)
|
||||
from_many?(true)
|
||||
sort(created_at: :asc)
|
||||
end
|
||||
|
||||
has_one :second_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||
public?(true)
|
||||
destination_attribute(:channel_id)
|
||||
from_many?(true)
|
||||
sort(created_at: :desc)
|
||||
end
|
||||
|
||||
has_one :dm_channel, AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||
api(AshPostgres.Test.ComplexCalculations.Api)
|
||||
public?(true)
|
||||
domain(AshPostgres.Test.ComplexCalculations.Domain)
|
||||
destination_attribute(:id)
|
||||
end
|
||||
|
||||
has_one :dm_channel_with_same_id, AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||
public?(true)
|
||||
no_attributes?(true)
|
||||
from_many?(true)
|
||||
filter(expr(parent(id) == id))
|
||||
api(AshPostgres.Test.ComplexCalculations.Api)
|
||||
domain(AshPostgres.Test.ComplexCalculations.Domain)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
defmodule AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [Ash.Policy.Authorizer]
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
|
||||
create_timestamp(:created_at, private?: false)
|
||||
update_timestamp(:updated_at, private?: false)
|
||||
create_timestamp(:created_at, public?: true)
|
||||
update_timestamp(:updated_at, public?: true)
|
||||
end
|
||||
|
||||
postgres do
|
||||
|
@ -21,8 +24,8 @@ defmodule AshPostgres.Test.ComplexCalculations.ChannelMember do
|
|||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
defmodule AshPostgres.Test.ComplexCalculations.DMChannel do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [Ash.Policy.Authorizer]
|
||||
|
||||
require Ash.Expr
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
|
||||
create_timestamp(:created_at, private?: false)
|
||||
update_timestamp(:updated_at, private?: false)
|
||||
create_timestamp(:created_at, public?: true)
|
||||
update_timestamp(:updated_at, public?: true)
|
||||
end
|
||||
|
||||
postgres do
|
||||
|
@ -24,16 +27,19 @@ defmodule AshPostgres.Test.ComplexCalculations.DMChannel do
|
|||
|
||||
relationships do
|
||||
has_many :channel_members, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||
public?(true)
|
||||
destination_attribute(:channel_id)
|
||||
end
|
||||
|
||||
has_one :first_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||
public?(true)
|
||||
destination_attribute(:channel_id)
|
||||
from_many?(true)
|
||||
sort(created_at: :asc)
|
||||
end
|
||||
|
||||
has_one :second_member, AshPostgres.Test.ComplexCalculations.ChannelMember do
|
||||
public?(true)
|
||||
destination_attribute(:channel_id)
|
||||
from_many?(true)
|
||||
sort(created_at: :desc)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
||||
@moduledoc false
|
||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -15,12 +19,13 @@ defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
|||
constraints: [
|
||||
one_of: [:demonstrated, :performed, :approved, :reopened]
|
||||
],
|
||||
public?: true,
|
||||
allow_nil?: false
|
||||
)
|
||||
|
||||
attribute(:documented_at, :utc_datetime_usec)
|
||||
create_timestamp(:inserted_at, private?: false)
|
||||
update_timestamp(:updated_at, private?: false)
|
||||
attribute(:documented_at, :utc_datetime_usec, public?: true)
|
||||
create_timestamp(:inserted_at, public?: true, writable?: true)
|
||||
update_timestamp(:updated_at, public?: true, writable?: true)
|
||||
end
|
||||
|
||||
calculations do
|
||||
|
@ -43,6 +48,8 @@ defmodule AshPostgres.Test.ComplexCalculations.Documentation do
|
|||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:skill, AshPostgres.Test.ComplexCalculations.Skill)
|
||||
belongs_to(:skill, AshPostgres.Test.ComplexCalculations.Skill) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
||||
@moduledoc false
|
||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.ComplexCalculations.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -14,7 +18,7 @@ defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:removed, :boolean, default: false, allow_nil?: false)
|
||||
attribute(:removed, :boolean, default: false, allow_nil?: false, public?: true)
|
||||
end
|
||||
|
||||
calculations do
|
||||
|
@ -37,13 +41,17 @@ defmodule AshPostgres.Test.ComplexCalculations.Skill do
|
|||
end
|
||||
|
||||
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
|
||||
public?(true)
|
||||
sort(timestamp: :desc, inserted_at: :desc)
|
||||
end
|
||||
|
||||
has_one :latest_documentation, AshPostgres.Test.ComplexCalculations.Documentation do
|
||||
public?(true)
|
||||
sort(timestamp: :desc, inserted_at: :desc)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.Test.Concat do
|
||||
@moduledoc false
|
||||
use Ash.Calculation
|
||||
use Ash.Resource.Calculation
|
||||
require Ash.Query
|
||||
|
||||
def init(opts) do
|
||||
|
@ -11,16 +11,16 @@ defmodule AshPostgres.Test.Concat do
|
|||
end
|
||||
end
|
||||
|
||||
def expression(opts, %{separator: separator}) do
|
||||
def expression(opts, %{arguments: %{separator: separator}}) do
|
||||
Enum.reduce(opts[:keys], nil, fn key, expr ->
|
||||
if expr do
|
||||
if separator do
|
||||
Ash.Query.expr(^expr <> ^separator <> ref(^key))
|
||||
expr(^expr <> ^separator <> ^ref(key))
|
||||
else
|
||||
Ash.Query.expr(^expr <> ref(^key))
|
||||
expr(^expr <> ^ref(key))
|
||||
end
|
||||
else
|
||||
Ash.Query.expr(ref(^key))
|
||||
expr(^ref(key))
|
||||
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
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.MultitenancyTest.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
identities do
|
||||
|
@ -9,10 +10,12 @@ defmodule AshPostgres.MultitenancyTest.Org do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:name, :string)
|
||||
attribute(:name, :string, public?: true)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -33,8 +36,15 @@ defmodule AshPostgres.MultitenancyTest.Org do
|
|||
end
|
||||
|
||||
relationships do
|
||||
has_many(:posts, AshPostgres.MultitenancyTest.Post, destination_attribute: :org_id)
|
||||
has_many(:users, AshPostgres.MultitenancyTest.User, destination_attribute: :org_id)
|
||||
has_many(:posts, AshPostgres.MultitenancyTest.Post,
|
||||
destination_attribute: :org_id,
|
||||
public?: true
|
||||
)
|
||||
|
||||
has_many(:users, AshPostgres.MultitenancyTest.User,
|
||||
destination_attribute: :org_id,
|
||||
public?: true
|
||||
)
|
||||
end
|
||||
|
||||
def tenant("org_" <> tenant) do
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.MultitenancyTest.Post do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.MultitenancyTest.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [Ash.Policy.Authorizer]
|
||||
|
||||
|
@ -17,10 +18,12 @@ defmodule AshPostgres.MultitenancyTest.Post do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:name, :string)
|
||||
attribute(:name, :string, public?: true)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
|
||||
update(:update_with_policy)
|
||||
|
@ -38,8 +41,14 @@ defmodule AshPostgres.MultitenancyTest.Post do
|
|||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:org, AshPostgres.MultitenancyTest.Org)
|
||||
belongs_to(:user, AshPostgres.MultitenancyTest.User)
|
||||
has_one(:self, __MODULE__, destination_attribute: :id, source_attribute: :id)
|
||||
belongs_to(:org, AshPostgres.MultitenancyTest.Org) do
|
||||
public?(true)
|
||||
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
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
defmodule AshPostgres.MultitenancyTest.User do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.MultitenancyTest.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:name, :string)
|
||||
attribute(:org_id, :uuid)
|
||||
attribute(:name, :string, public?: true)
|
||||
attribute(:org_id, :uuid, public?: true)
|
||||
end
|
||||
|
||||
postgres do
|
||||
|
@ -15,6 +16,8 @@ defmodule AshPostgres.MultitenancyTest.User do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -28,7 +31,9 @@ defmodule AshPostgres.MultitenancyTest.User do
|
|||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:org, AshPostgres.MultitenancyTest.Org)
|
||||
belongs_to(:org, AshPostgres.MultitenancyTest.Org) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|> Ash.Query.filter(post_id in ^post_ids)
|
||||
|> 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)}
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
defmodule AshPostgres.Test.Account do
|
||||
@moduledoc false
|
||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -12,7 +16,7 @@ defmodule AshPostgres.Test.Account do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:is_active, :boolean)
|
||||
attribute(:is_active, :boolean, public?: true)
|
||||
end
|
||||
|
||||
calculations do
|
||||
|
@ -30,6 +34,8 @@ defmodule AshPostgres.Test.Account do
|
|||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:user, AshPostgres.Test.User)
|
||||
belongs_to(:user, AshPostgres.Test.User) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
defmodule AshPostgres.Test.Author do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
defmodule RuntimeFullName do
|
||||
@moduledoc false
|
||||
use Ash.Calculation
|
||||
use Ash.Resource.Calculation
|
||||
|
||||
def calculate(records, _, _) do
|
||||
Enum.map(records, fn record ->
|
||||
|
@ -21,21 +22,29 @@ defmodule AshPostgres.Test.Author do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:first_name, :string)
|
||||
attribute(:last_name, :string)
|
||||
attribute(:bio, AshPostgres.Test.Bio)
|
||||
attribute(:badges, {:array, :atom})
|
||||
attribute(:first_name, :string, public?: true)
|
||||
attribute(:last_name, :string, public?: true)
|
||||
attribute(:bio, AshPostgres.Test.Bio, public?: true)
|
||||
attribute(:badges, {:array, :atom}, public?: true)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
relationships do
|
||||
has_one(:profile, AshPostgres.Test.Profile)
|
||||
has_many(:posts, AshPostgres.Test.Post)
|
||||
has_one(:profile, AshPostgres.Test.Profile) do
|
||||
public?(true)
|
||||
end
|
||||
|
||||
has_many(:posts, AshPostgres.Test.Post) do
|
||||
public?(true)
|
||||
end
|
||||
|
||||
has_many :authors_with_same_first_name, __MODULE__ do
|
||||
public?(true)
|
||||
source_attribute(:first_name)
|
||||
destination_attribute(:first_name)
|
||||
filter(expr(parent(id) != id))
|
||||
|
|
|
@ -3,15 +3,18 @@ defmodule AshPostgres.Test.Bio do
|
|||
use Ash.Resource, data_layer: :embedded
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
attribute(:title, :string)
|
||||
attribute(:bio, :string)
|
||||
attribute(:years_of_experience, :integer)
|
||||
attribute(:title, :string, public?: true)
|
||||
attribute(:bio, :string, public?: true)
|
||||
attribute(:years_of_experience, :integer, public?: true)
|
||||
|
||||
attribute :list_of_strings, {:array, :string} do
|
||||
public?(true)
|
||||
allow_nil?(true)
|
||||
default(nil)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.Comment do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [
|
||||
Ash.Policy.Authorizer
|
||||
|
@ -23,6 +24,7 @@ defmodule AshPostgres.Test.Comment do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
defaults([:read, :update, :destroy])
|
||||
|
||||
create :create do
|
||||
|
@ -35,10 +37,10 @@ defmodule AshPostgres.Test.Comment do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:title, :string)
|
||||
attribute(:likes, :integer)
|
||||
attribute(:arbitrary_timestamp, :utc_datetime_usec)
|
||||
create_timestamp(:created_at, writable?: true)
|
||||
attribute(:title, :string, public?: true)
|
||||
attribute(:likes, :integer, public?: true)
|
||||
attribute(:arbitrary_timestamp, :utc_datetime_usec, public?: true)
|
||||
create_timestamp(:created_at, writable?: true, public?: true)
|
||||
end
|
||||
|
||||
aggregates do
|
||||
|
@ -49,15 +51,22 @@ defmodule AshPostgres.Test.Comment do
|
|||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:post, AshPostgres.Test.Post)
|
||||
belongs_to(:author, AshPostgres.Test.Author)
|
||||
belongs_to(:post, AshPostgres.Test.Post) do
|
||||
public?(true)
|
||||
end
|
||||
|
||||
belongs_to(:author, AshPostgres.Test.Author) do
|
||||
public?(true)
|
||||
end
|
||||
|
||||
has_many(:ratings, AshPostgres.Test.Rating,
|
||||
public?: true,
|
||||
destination_attribute: :resource_id,
|
||||
relationship_context: %{data_layer: %{table: "comment_ratings"}}
|
||||
)
|
||||
|
||||
has_many(:popular_ratings, AshPostgres.Test.Rating,
|
||||
public?: true,
|
||||
destination_attribute: :resource_id,
|
||||
relationship_context: %{data_layer: %{table: "comment_ratings"}},
|
||||
filter: expr(score > 5)
|
||||
|
|
|
@ -2,14 +2,15 @@ defmodule AshPostgres.Test.Entity do
|
|||
@moduledoc false
|
||||
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
attributes do
|
||||
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
|
||||
|
||||
postgres do
|
||||
|
@ -18,6 +19,8 @@ defmodule AshPostgres.Test.Entity do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read])
|
||||
|
||||
read :read_from_temp do
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.IntegerPost do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -9,11 +10,13 @@ defmodule AshPostgres.Test.IntegerPost do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
integer_primary_key(:id)
|
||||
attribute(:title, :string)
|
||||
attribute(:title, :string, public?: true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.Manager do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -9,6 +10,8 @@ defmodule AshPostgres.Test.Manager do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:read, :update, :destroy])
|
||||
|
||||
create :create do
|
||||
|
@ -25,14 +28,15 @@ defmodule AshPostgres.Test.Manager do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:name, :string)
|
||||
attribute(:code, :string, allow_nil?: false)
|
||||
attribute(:must_be_present, :string, allow_nil?: false)
|
||||
attribute(:role, :string)
|
||||
attribute(:name, :string, public?: true)
|
||||
attribute(:code, :string, allow_nil?: false, public?: true)
|
||||
attribute(:must_be_present, :string, allow_nil?: false, public?: true)
|
||||
attribute(:role, :string, public?: true)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :organization, AshPostgres.Test.Organization do
|
||||
public?(true)
|
||||
attribute_writable?(true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.Organization do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [
|
||||
Ash.Policy.Authorizer
|
||||
|
@ -24,17 +25,27 @@ defmodule AshPostgres.Test.Organization do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:name, :string)
|
||||
attribute(:name, :string, public?: true)
|
||||
end
|
||||
|
||||
relationships do
|
||||
has_many(:users, AshPostgres.Test.User)
|
||||
has_many(:posts, AshPostgres.Test.Post)
|
||||
has_many(:managers, AshPostgres.Test.Manager)
|
||||
has_many(:users, AshPostgres.Test.User) do
|
||||
public?(true)
|
||||
end
|
||||
|
||||
has_many(:posts, AshPostgres.Test.Post) do
|
||||
public?(true)
|
||||
end
|
||||
|
||||
has_many(:managers, AshPostgres.Test.Manager) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ end
|
|||
defmodule AshPostgres.Test.Post do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [
|
||||
Ash.Policy.Authorizer
|
||||
|
@ -73,7 +74,14 @@ defmodule AshPostgres.Test.Post do
|
|||
end
|
||||
|
||||
actions do
|
||||
defaults([:update, :destroy])
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:destroy])
|
||||
|
||||
update :update do
|
||||
primary?(true)
|
||||
require_atomic?(false)
|
||||
end
|
||||
|
||||
read :title_is_foo do
|
||||
filter(expr(title == "foo"))
|
||||
|
@ -113,6 +121,7 @@ defmodule AshPostgres.Test.Post do
|
|||
end
|
||||
|
||||
update :manual_update do
|
||||
require_atomic?(false)
|
||||
manual(AshPostgres.Test.Post.ManualUpdate)
|
||||
end
|
||||
end
|
||||
|
@ -125,83 +134,103 @@ defmodule AshPostgres.Test.Post do
|
|||
uuid_primary_key(:id, writable?: true)
|
||||
|
||||
attribute(:title, :string) do
|
||||
public?(true)
|
||||
source(:title_column)
|
||||
end
|
||||
|
||||
attribute(:score, :integer)
|
||||
attribute(:public, :boolean)
|
||||
attribute(:category, :ci_string)
|
||||
attribute(:type, :atom, default: :sponsored, private?: true, writable?: false)
|
||||
attribute(:price, :integer)
|
||||
attribute(:decimal, :decimal, default: Decimal.new(0))
|
||||
attribute(:status, AshPostgres.Test.Types.Status)
|
||||
attribute(:status_enum, AshPostgres.Test.Types.StatusEnum)
|
||||
attribute(:status_enum_no_cast, AshPostgres.Test.Types.StatusEnumNoCast, source: :status_enum)
|
||||
attribute(:point, AshPostgres.Test.Point)
|
||||
attribute(:composite_point, AshPostgres.Test.CompositePoint)
|
||||
attribute(:stuff, :map)
|
||||
attribute(:list_of_stuff, {:array, :map})
|
||||
attribute(:uniq_one, :string)
|
||||
attribute(:uniq_two, :string)
|
||||
attribute(:uniq_custom_one, :string)
|
||||
attribute(:uniq_custom_two, :string)
|
||||
attribute(:score, :integer, public?: true)
|
||||
attribute(:public, :boolean, public?: true)
|
||||
attribute(:category, :ci_string, public?: true)
|
||||
attribute(:type, :atom, default: :sponsored, writable?: false, public?: false)
|
||||
attribute(:price, :integer, public?: true)
|
||||
attribute(:decimal, :decimal, default: Decimal.new(0), public?: true)
|
||||
attribute(:status, AshPostgres.Test.Types.Status, public?: true)
|
||||
attribute(:status_enum, AshPostgres.Test.Types.StatusEnum, public?: true)
|
||||
|
||||
attribute(:status_enum_no_cast, AshPostgres.Test.Types.StatusEnumNoCast,
|
||||
source: :status_enum,
|
||||
public?: true
|
||||
)
|
||||
|
||||
attribute(:point, AshPostgres.Test.Point, public?: true)
|
||||
attribute(:composite_point, AshPostgres.Test.CompositePoint, public?: true)
|
||||
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
|
||||
public?(true)
|
||||
constraints(nil_items?: true)
|
||||
end
|
||||
|
||||
create_timestamp(:created_at)
|
||||
update_timestamp(:updated_at)
|
||||
create_timestamp(:created_at, writable?: true, public?: true)
|
||||
update_timestamp(:updated_at, writable?: true, public?: true)
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define_for(AshPostgres.Test.Api)
|
||||
define(:create, args: [:title])
|
||||
define(:get_by_id, action: :read, get_by: [:id])
|
||||
define(:increment_score, args: [{:optional, :amount}])
|
||||
define(:destroy)
|
||||
define(:bulk_create, bulk?: true, action: :create)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :organization, AshPostgres.Test.Organization do
|
||||
public?(true)
|
||||
attribute_writable?(true)
|
||||
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
|
||||
public?(true)
|
||||
no_attributes?(true)
|
||||
filter(expr(parent(title) == title and parent(id) != id))
|
||||
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
|
||||
public?(true)
|
||||
filter(expr(title == parent_expr(title)))
|
||||
end
|
||||
|
||||
has_many :popular_comments, AshPostgres.Test.Comment do
|
||||
public?(true)
|
||||
destination_attribute(:post_id)
|
||||
filter(expr(likes > 10))
|
||||
end
|
||||
|
||||
has_many :comments_containing_title, AshPostgres.Test.Comment do
|
||||
public?(true)
|
||||
manual(AshPostgres.Test.Post.CommentsContainingTitle)
|
||||
end
|
||||
|
||||
has_many :comments_with_high_rating, AshPostgres.Test.Comment do
|
||||
public?(true)
|
||||
filter(expr(ratings.score > 5))
|
||||
end
|
||||
|
||||
has_many(:ratings, AshPostgres.Test.Rating,
|
||||
public?: true,
|
||||
destination_attribute: :resource_id,
|
||||
relationship_context: %{data_layer: %{table: "post_ratings"}}
|
||||
)
|
||||
|
||||
has_many(:post_links, AshPostgres.Test.PostLink,
|
||||
public?: true,
|
||||
destination_attribute: :source_post_id,
|
||||
filter: [state: :active]
|
||||
)
|
||||
|
||||
many_to_many(:linked_posts, __MODULE__,
|
||||
public?: true,
|
||||
through: AshPostgres.Test.PostLink,
|
||||
join_relationship: :post_links,
|
||||
source_attribute_on_join_resource: :source_post_id,
|
||||
|
@ -209,13 +238,16 @@ defmodule AshPostgres.Test.Post do
|
|||
)
|
||||
|
||||
many_to_many(:followers, AshPostgres.Test.User,
|
||||
public?: true,
|
||||
through: AshPostgres.Test.PostFollower,
|
||||
source_attribute_on_join_resource: :post_id,
|
||||
destination_attribute_on_join_resource: :follower_id,
|
||||
read_action: :active
|
||||
)
|
||||
|
||||
has_many(:views, AshPostgres.Test.PostView)
|
||||
has_many(:views, AshPostgres.Test.PostView) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
|
||||
validations do
|
||||
|
@ -483,10 +515,10 @@ end
|
|||
|
||||
defmodule CalculatePostPriceString do
|
||||
@moduledoc false
|
||||
use Ash.Calculation
|
||||
use Ash.Resource.Calculation
|
||||
|
||||
@impl true
|
||||
def select(_, _, _), do: [:price]
|
||||
def load(_, _, _), do: [:price]
|
||||
|
||||
@impl true
|
||||
def calculate(records, _, _) do
|
||||
|
@ -500,7 +532,7 @@ end
|
|||
|
||||
defmodule CalculatePostPriceStringWithSymbol do
|
||||
@moduledoc false
|
||||
use Ash.Calculation
|
||||
use Ash.Resource.Calculation
|
||||
|
||||
@impl true
|
||||
def load(_, _, _), do: [:price_string]
|
||||
|
@ -524,7 +556,7 @@ defmodule AshPostgres.Test.Post.ManualUpdate do
|
|||
|> Ash.Changeset.for_update(:update, changeset.attributes)
|
||||
|> Ash.Changeset.force_change_attribute(:title, "manual")
|
||||
|> Ash.Changeset.load(:comments)
|
||||
|> AshPostgres.Test.Api.update!()
|
||||
|> Ash.update!()
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.PostFollower do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -9,6 +10,8 @@ defmodule AshPostgres.Test.PostFollower do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -18,10 +21,12 @@ defmodule AshPostgres.Test.PostFollower do
|
|||
|
||||
relationships do
|
||||
belongs_to :post, AshPostgres.Test.Post do
|
||||
public?(true)
|
||||
allow_nil?(false)
|
||||
end
|
||||
|
||||
belongs_to :follower, AshPostgres.Test.User do
|
||||
public?(true)
|
||||
allow_nil?(false)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.PostLink do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -9,6 +10,8 @@ defmodule AshPostgres.Test.PostLink do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
|
@ -18,6 +21,7 @@ defmodule AshPostgres.Test.PostLink do
|
|||
|
||||
attributes do
|
||||
attribute :state, :atom do
|
||||
public?(true)
|
||||
constraints(one_of: [:active, :archived])
|
||||
default(:active)
|
||||
end
|
||||
|
@ -25,11 +29,13 @@ defmodule AshPostgres.Test.PostLink do
|
|||
|
||||
relationships do
|
||||
belongs_to :source_post, AshPostgres.Test.Post do
|
||||
public?(true)
|
||||
allow_nil?(false)
|
||||
primary_key?(true)
|
||||
end
|
||||
|
||||
belongs_to :destination_post, AshPostgres.Test.Post do
|
||||
public?(true)
|
||||
allow_nil?(false)
|
||||
primary_key?(true)
|
||||
end
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
defmodule AshPostgres.Test.PostView do
|
||||
@moduledoc false
|
||||
use Ash.Resource, data_layer: AshPostgres.DataLayer
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read])
|
||||
end
|
||||
|
||||
attributes do
|
||||
create_timestamp(:time)
|
||||
attribute(:browser, :atom, constraints: [one_of: [:firefox, :chrome, :edge]])
|
||||
attribute(:browser, :atom, constraints: [one_of: [:firefox, :chrome, :edge]], public?: true)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :post, AshPostgres.Test.Post do
|
||||
public?(true)
|
||||
allow_nil?(false)
|
||||
attribute_writable?(true)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.Profile do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -11,14 +12,18 @@ defmodule AshPostgres.Test.Profile do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
attribute(:description, :string)
|
||||
attribute(:description, :string, public?: true)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:author, AshPostgres.Test.Author)
|
||||
belongs_to(:author, AshPostgres.Test.Author) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.Rating do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
|
@ -9,12 +10,14 @@ defmodule AshPostgres.Test.Rating do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:score, :integer)
|
||||
attribute(:resource_id, :uuid)
|
||||
attribute(:score, :integer, public?: true)
|
||||
attribute(:resource_id, :uuid, public?: true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,20 +2,22 @@ defmodule AshPostgres.Test.Record do
|
|||
@moduledoc false
|
||||
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Domain,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
attributes do
|
||||
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
|
||||
|
||||
relationships do
|
||||
alias AshPostgres.Test.Entity
|
||||
|
||||
has_one :entity, Entity do
|
||||
public?(true)
|
||||
no_attributes?(true)
|
||||
|
||||
read_action(:read_from_temp)
|
||||
|
@ -30,6 +32,8 @@ defmodule AshPostgres.Test.Record do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@ defmodule AshPostgres.Test.Subquery.Access do
|
|||
alias AshPostgres.Test.Subquery.Parent
|
||||
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Subquery.ParentDomain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [
|
||||
Ash.Policy.Authorizer
|
||||
|
@ -17,19 +18,19 @@ defmodule AshPostgres.Test.Subquery.Access do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:parent_id, :uuid)
|
||||
attribute(:email, :string)
|
||||
attribute(:parent_id, :uuid, public?: true)
|
||||
attribute(:email, :string, public?: true)
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define_for(AshPostgres.Test.Subquery.ParentApi)
|
||||
|
||||
define(:create)
|
||||
define(:read)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:parent, Parent)
|
||||
belongs_to(:parent, Parent) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
|
@ -39,6 +40,8 @@ defmodule AshPostgres.Test.Subquery.Access do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :update, :destroy])
|
||||
|
||||
read :read do
|
||||
|
|
|
@ -3,6 +3,7 @@ defmodule AshPostgres.Test.Subquery.Child do
|
|||
alias AshPostgres.Test.Subquery.Through
|
||||
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Subquery.ChildDomain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [
|
||||
Ash.Policy.Authorizer
|
||||
|
@ -15,18 +16,17 @@ defmodule AshPostgres.Test.Subquery.Child do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:state, :string)
|
||||
attribute(:state, :string, public?: true)
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define_for(AshPostgres.Test.Subquery.ChildApi)
|
||||
|
||||
define(:create)
|
||||
define(:read)
|
||||
end
|
||||
|
||||
relationships do
|
||||
has_many :throughs, Through do
|
||||
public?(true)
|
||||
source_attribute(:id)
|
||||
destination_attribute(:child_id)
|
||||
end
|
||||
|
@ -48,6 +48,8 @@ defmodule AshPostgres.Test.Subquery.Child do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
defmodule AshPostgres.Test.Subquery.ChildApi do
|
||||
defmodule AshPostgres.Test.Subquery.ChildDomain do
|
||||
@moduledoc false
|
||||
alias AshPostgres.Test.Subquery.Child
|
||||
alias AshPostgres.Test.Subquery.Through
|
||||
use Ash.Api
|
||||
use Ash.Domain
|
||||
|
||||
resources do
|
||||
resource(Child)
|
||||
resource(Through)
|
||||
end
|
||||
|
||||
authorization do
|
||||
authorize(:when_requested)
|
||||
end
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
defmodule AshPostgres.Test.Subquery.Parent do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
domain: AshPostgres.Test.Subquery.ParentDomain,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
authorizers: [
|
||||
Ash.Policy.Authorizer
|
||||
|
@ -15,22 +16,25 @@ defmodule AshPostgres.Test.Subquery.Parent do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:owner_email, :string)
|
||||
attribute(:other_owner_email, :string)
|
||||
attribute(:visible, :boolean)
|
||||
attribute(:owner_email, :string, public?: true)
|
||||
attribute(:other_owner_email, :string, public?: true)
|
||||
attribute(:visible, :boolean, public?: true)
|
||||
end
|
||||
|
||||
relationships do
|
||||
many_to_many :children, Child do
|
||||
public?(true)
|
||||
through(Through)
|
||||
source_attribute(:id)
|
||||
source_attribute_on_join_resource(:parent_id)
|
||||
destination_attribute(:id)
|
||||
destination_attribute_on_join_resource(:child_id)
|
||||
api(AshPostgres.Test.Subquery.ChildApi)
|
||||
domain(AshPostgres.Test.Subquery.ChildDomain)
|
||||
end
|
||||
|
||||
has_many(:accesses, Access)
|
||||
has_many(:accesses, Access) do
|
||||
public?(true)
|
||||
end
|
||||
end
|
||||
|
||||
policies do
|
||||
|
@ -48,8 +52,6 @@ defmodule AshPostgres.Test.Subquery.Parent do
|
|||
end
|
||||
|
||||
code_interface do
|
||||
define_for(AshPostgres.Test.Subquery.ParentApi)
|
||||
|
||||
define(:create)
|
||||
define(:read)
|
||||
|
||||
|
@ -57,6 +59,8 @@ defmodule AshPostgres.Test.Subquery.Parent do
|
|||
end
|
||||
|
||||
actions do
|
||||
default_accept(:*)
|
||||
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
defmodule AshPostgres.Test.Subquery.ParentApi do
|
||||
defmodule AshPostgres.Test.Subquery.ParentDomain do
|
||||
@moduledoc false
|
||||
alias AshPostgres.Test.Subquery.Access
|
||||
alias AshPostgres.Test.Subquery.Parent
|
||||
use Ash.Api
|
||||
use Ash.Domain
|
||||
|
||||
resources do
|
||||
resource(Parent)
|
||||
resource(Access)
|
||||
end
|
||||
|
||||
authorization do
|
||||
authorize(:when_requested)
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue