2021-05-10 09:24:48 +12:00
|
|
|
defmodule Ash.Test.Actions.LoadTest do
|
2020-06-02 17:47:25 +12:00
|
|
|
@moduledoc false
|
2021-01-29 08:47:59 +13:00
|
|
|
use ExUnit.Case, async: false
|
2019-12-12 11:45:59 +13:00
|
|
|
|
2020-10-08 18:22:55 +13:00
|
|
|
require Ash.Query
|
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
alias Ash.Test.Domain, as: Domain
|
|
|
|
|
2022-09-15 16:54:30 +12:00
|
|
|
defmodule Campaign do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Resource,
|
2024-03-28 09:06:40 +13:00
|
|
|
domain: Domain,
|
2022-09-15 16:54:30 +12:00
|
|
|
data_layer: Ash.DataLayer.Ets
|
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults([:read, :destroy, create: :*, update: :*])
|
2022-09-15 16:54:30 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2023-06-08 13:54:17 +12:00
|
|
|
uuid_primary_key(:id)
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:name, :ci_string, public?: true)
|
2022-09-15 16:54:30 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
defmodule Bio do
|
2023-09-29 01:36:37 +13:00
|
|
|
use Ash.Resource,
|
|
|
|
authorizers: [Ash.Policy.Authorizer],
|
|
|
|
data_layer: :embedded
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
attributes do
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:first_name, :string, public?: true)
|
|
|
|
attribute(:last_name, :string, public?: true)
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
attribute :type, :string do
|
|
|
|
allow_nil?(false)
|
|
|
|
default("bio")
|
|
|
|
end
|
2023-09-29 03:23:16 +13:00
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute :forbidden_field, :string do
|
|
|
|
public?(true)
|
|
|
|
end
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
|
2023-09-29 01:36:37 +13:00
|
|
|
field_policies do
|
|
|
|
field_policy :* do
|
|
|
|
authorize_if always()
|
|
|
|
end
|
|
|
|
|
2023-09-29 03:23:16 +13:00
|
|
|
field_policy :forbidden_field do
|
|
|
|
forbid_if always()
|
|
|
|
end
|
|
|
|
|
2023-09-29 01:36:37 +13:00
|
|
|
field_policy :forbidden_name do
|
|
|
|
forbid_if always()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
policies do
|
|
|
|
policy always() do
|
|
|
|
authorize_if always()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
calculations do
|
2024-03-28 09:06:40 +13:00
|
|
|
calculate :full_name, :string, expr(first_name <> " " <> last_name) do
|
|
|
|
public?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
calculate :forbidden_name, :string, expr(first_name <> " " <> last_name) do
|
|
|
|
public?(true)
|
|
|
|
end
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule OtherKindOfBio do
|
2023-09-29 01:36:37 +13:00
|
|
|
use Ash.Resource,
|
|
|
|
authorizers: [Ash.Policy.Authorizer],
|
|
|
|
data_layer: :embedded
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
attributes do
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:first_name, :string, public?: true)
|
|
|
|
attribute(:last_name, :string, public?: true)
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
attribute :type, :string do
|
|
|
|
allow_nil?(false)
|
|
|
|
default("other_kind_of_bio")
|
|
|
|
end
|
2023-09-29 03:23:16 +13:00
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute :forbidden_field, :string do
|
|
|
|
public?(true)
|
|
|
|
end
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
|
2023-09-29 01:36:37 +13:00
|
|
|
field_policies do
|
|
|
|
field_policy :* do
|
|
|
|
authorize_if always()
|
|
|
|
end
|
|
|
|
|
2023-09-29 03:23:16 +13:00
|
|
|
field_policy :forbidden_field do
|
|
|
|
forbid_if always()
|
|
|
|
end
|
|
|
|
|
2023-09-29 01:36:37 +13:00
|
|
|
field_policy :forbidden_name do
|
|
|
|
forbid_if always()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
policies do
|
|
|
|
policy always() do
|
|
|
|
authorize_if always()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
calculations do
|
2024-03-28 09:06:40 +13:00
|
|
|
calculate :full_name, :string, expr(first_name <> " " <> last_name) do
|
|
|
|
public?(true)
|
|
|
|
end
|
2023-09-29 01:36:37 +13:00
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
calculate :forbidden_name, :string, expr(first_name <> " " <> last_name) do
|
|
|
|
public?(true)
|
|
|
|
end
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule BioUnion do
|
|
|
|
use Ash.Type.NewType,
|
|
|
|
subtype_of: :union,
|
|
|
|
constraints: [
|
|
|
|
types: [
|
|
|
|
bio: [
|
|
|
|
type: Bio,
|
|
|
|
tag: :type,
|
|
|
|
tag_value: "bio"
|
|
|
|
],
|
|
|
|
other_kind_of_bio: [
|
|
|
|
type: OtherKindOfBio,
|
|
|
|
tag: :type,
|
|
|
|
tag_value: "other_kind_of_bio"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2019-12-12 11:45:59 +13:00
|
|
|
defmodule Author do
|
2020-06-02 17:47:25 +12:00
|
|
|
@moduledoc false
|
2020-06-22 15:26:47 +12:00
|
|
|
use Ash.Resource,
|
2024-03-28 09:06:40 +13:00
|
|
|
domain: Domain,
|
2020-06-22 15:26:47 +12:00
|
|
|
data_layer: Ash.DataLayer.Ets,
|
|
|
|
authorizers: [
|
|
|
|
Ash.Test.Authorizer
|
|
|
|
]
|
2020-06-14 18:39:11 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults([:read, :destroy, create: :*, update: :*])
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2023-06-08 13:54:17 +12:00
|
|
|
uuid_primary_key(:id)
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:name, :string, public?: true)
|
|
|
|
attribute(:bio, Bio, public?: true)
|
|
|
|
attribute(:bio_union, BioUnion, public?: true)
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
calculations do
|
2024-03-28 09:06:40 +13:00
|
|
|
calculate :bio_union_calc, BioUnion, expr(bio_union) do
|
|
|
|
public?(true)
|
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2024-03-28 09:06:40 +13:00
|
|
|
has_many(:posts, Ash.Test.Actions.LoadTest.Post,
|
|
|
|
destination_attribute: :author_id,
|
|
|
|
public?: true
|
|
|
|
)
|
2021-05-07 16:28:51 +12:00
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
has_one(:latest_post, Ash.Test.Actions.LoadTest.Post,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute: :author_id,
|
2024-03-28 09:06:40 +13:00
|
|
|
sort: [inserted_at: :desc],
|
|
|
|
public?: true
|
2023-06-08 13:54:17 +12:00
|
|
|
)
|
2022-09-15 16:54:30 +12:00
|
|
|
|
|
|
|
belongs_to :campaign, Ash.Test.Actions.LoadTest.Campaign do
|
2023-06-08 13:54:17 +12:00
|
|
|
attribute_type(:ci_string)
|
|
|
|
source_attribute(:campaign_name)
|
|
|
|
destination_attribute(:name)
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
2022-09-15 16:54:30 +12:00
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-02-21 16:46:39 +13:00
|
|
|
defmodule PostsInSameCategory do
|
|
|
|
use Ash.Resource.ManualRelationship
|
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
def load(posts, _, %{query: destination_query, domain: domain}) do
|
2022-02-21 16:46:39 +13:00
|
|
|
categories = Enum.map(posts, & &1.category)
|
|
|
|
|
|
|
|
other_posts =
|
|
|
|
destination_query
|
|
|
|
|> Ash.Query.filter(category in ^categories)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(domain: domain)
|
2022-02-21 16:46:39 +13:00
|
|
|
|> Enum.group_by(& &1.category)
|
|
|
|
|
|
|
|
{:ok,
|
|
|
|
Map.new(posts, fn post ->
|
|
|
|
related_posts =
|
|
|
|
other_posts
|
|
|
|
|> Map.get(post.category, [])
|
|
|
|
|> Enum.reject(&(&1.id == post.id))
|
|
|
|
|
|
|
|
{Map.take(post, [:id]), related_posts}
|
|
|
|
end)}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-12 11:45:59 +13:00
|
|
|
defmodule Post do
|
2020-06-02 17:47:25 +12:00
|
|
|
@moduledoc false
|
2024-04-16 09:33:56 +12:00
|
|
|
use Ash.Resource,
|
|
|
|
domain: Domain,
|
|
|
|
authorizers: [Ash.Policy.Authorizer],
|
|
|
|
data_layer: Ash.DataLayer.Ets
|
2020-06-14 18:39:11 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults([:read, :destroy, create: :*, update: :*])
|
2024-04-16 09:33:56 +12:00
|
|
|
|
2024-05-04 21:56:46 +12:00
|
|
|
read :keyset do
|
|
|
|
pagination do
|
|
|
|
required? false
|
|
|
|
keyset? true
|
|
|
|
countable true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-11 03:48:10 +12:00
|
|
|
read :required_pagination do
|
|
|
|
pagination do
|
|
|
|
offset? true
|
|
|
|
default_limit 20
|
|
|
|
countable :by_default
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-04-16 09:33:56 +12:00
|
|
|
read :all_access
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2023-06-08 13:54:17 +12:00
|
|
|
uuid_primary_key(:id)
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:title, :string, public?: true)
|
|
|
|
attribute(:contents, :string, public?: true)
|
|
|
|
attribute(:category, :string, public?: true)
|
2024-04-16 09:33:56 +12:00
|
|
|
attribute(:secret, :string, public?: true)
|
2021-05-07 16:28:51 +12:00
|
|
|
timestamps()
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
|
2024-04-16 09:33:56 +12:00
|
|
|
policies do
|
|
|
|
policy always() do
|
|
|
|
authorize_if always()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
field_policies do
|
|
|
|
field_policy :* do
|
|
|
|
authorize_if always()
|
|
|
|
end
|
|
|
|
|
|
|
|
field_policy :secret do
|
|
|
|
authorize_if action([:create, :all_access])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-10-29 04:18:01 +13:00
|
|
|
code_interface do
|
|
|
|
define :get_by_id do
|
2023-06-08 13:54:17 +12:00
|
|
|
action(:read)
|
|
|
|
get_by([:id])
|
2022-10-29 04:18:01 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-12 11:45:59 +13:00
|
|
|
relationships do
|
2024-01-19 18:05:42 +13:00
|
|
|
belongs_to :author, Author do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
2024-01-19 18:05:42 +13:00
|
|
|
end
|
2020-06-22 15:26:47 +12:00
|
|
|
|
2022-02-21 16:46:39 +13:00
|
|
|
has_many :posts_in_same_category, __MODULE__ do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
2023-06-08 13:54:17 +12:00
|
|
|
manual(PostsInSameCategory)
|
2022-02-21 16:46:39 +13:00
|
|
|
end
|
|
|
|
|
2022-04-04 17:48:37 +12:00
|
|
|
has_many :ratings, Ash.Test.Actions.LoadTest.Rating do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
|
|
|
domain(Ash.Test.Actions.LoadTest.Domain2)
|
2022-04-04 17:48:37 +12:00
|
|
|
end
|
|
|
|
|
2023-07-27 08:46:22 +12:00
|
|
|
has_many :posts_with_same_title, __MODULE__ do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
2023-07-27 08:46:22 +12:00
|
|
|
no_attributes? true
|
|
|
|
filter expr(parent(title) == title and parent(id) != id)
|
|
|
|
end
|
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
many_to_many(:categories, Ash.Test.Actions.LoadTest.Category,
|
2024-03-28 09:06:40 +13:00
|
|
|
public?: true,
|
2021-05-10 09:24:48 +12:00
|
|
|
through: Ash.Test.Actions.LoadTest.PostCategory,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute_on_join_resource: :category_id,
|
|
|
|
source_attribute_on_join_resource: :post_id
|
2023-06-08 13:54:17 +12:00
|
|
|
)
|
2020-06-22 15:26:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule PostCategory do
|
|
|
|
@moduledoc false
|
2024-03-28 09:06:40 +13:00
|
|
|
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults([:read, :destroy, create: :*, update: :*])
|
2020-06-22 15:26:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2024-03-28 09:06:40 +13:00
|
|
|
belongs_to(:post, Post, primary_key?: true, allow_nil?: false, public?: true)
|
2021-01-13 09:40:55 +13:00
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
belongs_to(:category, Ash.Test.Actions.LoadTest.Category,
|
2021-01-13 09:40:55 +13:00
|
|
|
primary_key?: true,
|
2024-03-28 09:06:40 +13:00
|
|
|
allow_nil?: false,
|
|
|
|
public?: true
|
2023-06-08 13:54:17 +12:00
|
|
|
)
|
2020-06-22 15:26:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule Category do
|
2021-04-19 11:41:49 +12:00
|
|
|
@moduledoc false
|
2024-03-28 09:06:40 +13:00
|
|
|
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults([:read, :destroy, create: :*, update: :*])
|
2024-05-04 21:56:46 +12:00
|
|
|
|
|
|
|
read :keyset do
|
|
|
|
pagination do
|
|
|
|
required? false
|
|
|
|
keyset? true
|
|
|
|
end
|
|
|
|
end
|
2020-06-22 15:26:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
2023-06-08 13:54:17 +12:00
|
|
|
uuid_primary_key(:id)
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:name, :string, public?: true)
|
2020-06-22 15:26:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2023-06-08 13:54:17 +12:00
|
|
|
many_to_many(:posts, Post,
|
2024-03-28 09:06:40 +13:00
|
|
|
public?: true,
|
2020-06-22 15:26:47 +12:00
|
|
|
through: PostCategory,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute_on_join_resource: :post_id,
|
|
|
|
source_attribute_on_join_resource: :category_id
|
2023-06-08 13:54:17 +12:00
|
|
|
)
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-04-04 17:48:37 +12:00
|
|
|
defmodule Rating do
|
|
|
|
use Ash.Resource,
|
2024-03-28 09:06:40 +13:00
|
|
|
domain: Ash.Test.Actions.LoadTest.Domain2,
|
2022-04-04 17:48:37 +12:00
|
|
|
data_layer: Ash.DataLayer.Ets
|
|
|
|
|
2022-06-21 09:01:28 +12:00
|
|
|
ets do
|
2023-06-08 13:54:17 +12:00
|
|
|
private?(true)
|
2022-06-21 09:01:28 +12:00
|
|
|
end
|
|
|
|
|
2022-04-04 17:48:37 +12:00
|
|
|
attributes do
|
2023-06-08 13:54:17 +12:00
|
|
|
uuid_primary_key(:id)
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute(:rating, :integer, public?: true)
|
2022-04-04 17:48:37 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults([:read, :destroy, create: :*, update: :*])
|
2022-04-04 17:48:37 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
|
|
|
belongs_to :post, Post do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
|
|
|
domain(Domain)
|
2022-04-04 17:48:37 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
defmodule Domain2 do
|
2022-04-04 17:48:37 +12:00
|
|
|
@moduledoc false
|
2024-03-28 09:06:40 +13:00
|
|
|
use Ash.Domain
|
2019-12-12 11:45:59 +13:00
|
|
|
|
2020-06-14 18:39:11 +12:00
|
|
|
resources do
|
2024-03-28 09:06:40 +13:00
|
|
|
resource Rating
|
2020-06-14 18:39:11 +12:00
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
|
|
|
|
2020-06-22 15:26:47 +12:00
|
|
|
setup do
|
2021-01-29 08:47:59 +13:00
|
|
|
start_supervised(
|
|
|
|
{Ash.Test.Authorizer,
|
|
|
|
strict_check: :authorized,
|
|
|
|
check: {:error, Ash.Error.Forbidden.exception([])},
|
|
|
|
strict_check_context: [:query]}
|
|
|
|
)
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
2021-05-10 09:24:48 +12:00
|
|
|
describe "loads" do
|
2022-02-21 16:46:39 +13:00
|
|
|
test "it allows loading manual relationships" do
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", category: "foo"})
|
|
|
|
|> Ash.create!()
|
2022-02-21 16:46:39 +13:00
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", category: "bar"})
|
|
|
|
|> Ash.create!()
|
2022-02-21 16:46:39 +13:00
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", category: "foo"})
|
|
|
|
|> Ash.create!()
|
2022-02-21 16:46:39 +13:00
|
|
|
|
|
|
|
post3_id = post3.id
|
|
|
|
|
|
|
|
assert [%{id: ^post3_id}] =
|
|
|
|
post1
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!(:posts_in_same_category)
|
2022-02-21 16:46:39 +13:00
|
|
|
|> Map.get(:posts_in_same_category)
|
|
|
|
end
|
|
|
|
|
2023-07-27 08:46:22 +12:00
|
|
|
test "parent expressions can be used for complex constraints" do
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", category: "foo"})
|
|
|
|
|> Ash.create!()
|
2023-07-27 08:46:22 +12:00
|
|
|
|
|
|
|
post1_same =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", category: "bar"})
|
|
|
|
|> Ash.create!()
|
2023-07-27 08:46:22 +12:00
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", category: "baz"})
|
|
|
|
|> Ash.create!()
|
2023-07-27 08:46:22 +12:00
|
|
|
|
|
|
|
post1_same_id = post1_same.id
|
|
|
|
|
|
|
|
assert [%{id: ^post1_same_id}] =
|
|
|
|
post1
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!(:posts_with_same_title)
|
2023-07-27 08:46:22 +12:00
|
|
|
|> Map.get(:posts_with_same_title)
|
|
|
|
end
|
|
|
|
|
2022-12-07 19:10:34 +13:00
|
|
|
test "it allows loading through manual relationships" do
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", category: "foo"})
|
|
|
|
|> Ash.create!()
|
2022-12-07 19:10:34 +13:00
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", category: "bar"})
|
|
|
|
|> Ash.create!()
|
2022-12-07 19:10:34 +13:00
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", category: "foo"})
|
|
|
|
|> Ash.create!()
|
2022-12-07 19:10:34 +13:00
|
|
|
|
|
|
|
post3_id = post3.id
|
|
|
|
|
|
|
|
assert [%{id: ^post3_id}] =
|
|
|
|
post1
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!(posts_in_same_category: :ratings)
|
2022-12-07 19:10:34 +13:00
|
|
|
|> Map.get(:posts_in_same_category)
|
|
|
|
end
|
|
|
|
|
2022-10-05 04:22:24 +13:00
|
|
|
test "it uses `Comp.equal?/2` to support things like ci_string foreign keys" do
|
2022-09-15 16:54:30 +12:00
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg", campaign_name: "FrEd"})
|
|
|
|
|> Ash.create!()
|
2022-09-15 16:54:30 +12:00
|
|
|
|
|
|
|
Campaign
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "fReD"})
|
|
|
|
|> Ash.create!()
|
2022-09-15 16:54:30 +12:00
|
|
|
|
|
|
|
assert %{
|
|
|
|
campaign: %{name: %Ash.CiString{string: "fReD"}},
|
|
|
|
campaign_name: %Ash.CiString{string: "FrEd"}
|
2024-03-28 09:06:40 +13:00
|
|
|
} = Ash.load!(author, :campaign)
|
2022-09-15 16:54:30 +12:00
|
|
|
end
|
|
|
|
|
2024-01-19 18:05:42 +13:00
|
|
|
test "it allows loading data" do
|
2020-07-12 18:25:53 +12:00
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2019-12-12 11:45:59 +13:00
|
|
|
|
2019-12-20 18:30:27 +13:00
|
|
|
post1 =
|
2020-07-12 18:25:53 +12:00
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", author_id: author.id})
|
|
|
|
|> Ash.create!()
|
2019-12-12 11:45:59 +13:00
|
|
|
|
2019-12-20 18:30:27 +13:00
|
|
|
post2 =
|
2020-07-12 18:25:53 +12:00
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", author_id: author.id})
|
|
|
|
|> Ash.create!()
|
2024-01-19 18:05:42 +13:00
|
|
|
|
|
|
|
assert [fetched_post1, fetched_post2] =
|
|
|
|
author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!(:posts)
|
2024-01-19 18:05:42 +13:00
|
|
|
|> Map.get(:posts)
|
|
|
|
|
|
|
|
assert Enum.sort([post1.id, post2.id]) == Enum.sort([fetched_post1.id, fetched_post2.id])
|
|
|
|
end
|
|
|
|
|
|
|
|
test "it allows loading nested related data" do
|
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2024-01-19 18:05:42 +13:00
|
|
|
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", author_id: author.id})
|
|
|
|
|> Ash.create!()
|
2024-01-19 18:05:42 +13:00
|
|
|
|
|
|
|
post2 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2", author_id: author.id})
|
|
|
|
|> Ash.create!()
|
2019-12-12 11:45:59 +13:00
|
|
|
|
2020-05-14 03:54:44 +12:00
|
|
|
[author] =
|
|
|
|
Author
|
2020-08-26 16:16:08 +12:00
|
|
|
|> Ash.Query.load(posts: [:author])
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(posts.id == ^post1.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2019-12-12 11:45:59 +13:00
|
|
|
|
2019-12-20 18:30:27 +13:00
|
|
|
assert Enum.sort(Enum.map(author.posts, &Map.get(&1, :id))) ==
|
|
|
|
Enum.sort([post1.id, post2.id])
|
2019-12-20 17:19:34 +13:00
|
|
|
|
2019-12-20 18:30:27 +13:00
|
|
|
for post <- author.posts do
|
|
|
|
assert post.author.id == author.id
|
|
|
|
end
|
|
|
|
end
|
2020-06-22 15:26:47 +12:00
|
|
|
|
2024-04-16 09:33:56 +12:00
|
|
|
test "it allows using a custom read action for related data" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post", secret: "42", author_id: author.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
2024-04-16 23:06:48 +12:00
|
|
|
all_access_posts_query = Ash.Query.for_read(Post, :all_access)
|
2024-04-16 09:33:56 +12:00
|
|
|
|
|
|
|
assert [%{secret: "42"}] =
|
|
|
|
author
|
|
|
|
|> Ash.load!(posts: all_access_posts_query)
|
|
|
|
|> Map.get(:posts)
|
|
|
|
end
|
|
|
|
|
2022-07-12 14:59:51 +12:00
|
|
|
test "unloading related data sets it back to `%Ash.NotLoaded{}`" do
|
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
[author] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(posts: [:author])
|
|
|
|
|> Ash.Query.filter(posts.id == ^post1.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
assert author
|
2022-08-16 08:02:22 +12:00
|
|
|
|> Ash.Resource.unload([:posts, :author])
|
2022-07-12 14:59:51 +12:00
|
|
|
|> Map.get(:posts)
|
|
|
|
|> Enum.all?(fn post ->
|
|
|
|
%Ash.NotLoaded{} = post.author
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "loading something does not unload previously loaded things" do
|
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
[author] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(:posts)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
|
|
|
|> Ash.load!(:latest_post)
|
2022-07-12 14:59:51 +12:00
|
|
|
|
|
|
|
refute match?(%Ash.NotLoaded{}, author.posts)
|
|
|
|
refute match?(%Ash.NotLoaded{}, author.latest_post)
|
|
|
|
end
|
|
|
|
|
2022-05-19 05:11:30 +12:00
|
|
|
test "loading something already loaded still loads it unless lazy?: true" do
|
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
post2 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
[author] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(posts: [:author])
|
|
|
|
|> Ash.Query.filter(posts.id == ^post1.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
assert Enum.sort(Enum.map(author.posts, &Map.get(&1, :id))) ==
|
|
|
|
Enum.sort([post1.id, post2.id])
|
|
|
|
|
|
|
|
for post <- author.posts do
|
|
|
|
assert post.author.id == author.id
|
|
|
|
end
|
|
|
|
|
|
|
|
post3 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post3"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
author =
|
|
|
|
author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!([posts: [:author]], authorize?: true)
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
assert Enum.sort(Enum.map(author.posts, &Map.get(&1, :id))) ==
|
|
|
|
Enum.sort([post1.id, post2.id, post3.id])
|
|
|
|
|
|
|
|
for post <- author.posts do
|
|
|
|
assert post.author.id == author.id
|
|
|
|
end
|
|
|
|
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post4"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
author =
|
|
|
|
author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!([posts: [:author]], authorize?: true, lazy?: true)
|
2022-05-19 05:11:30 +12:00
|
|
|
|
|
|
|
assert Enum.sort(Enum.map(author.posts, &Map.get(&1, :id))) ==
|
|
|
|
Enum.sort([post1.id, post2.id, post3.id])
|
|
|
|
|
|
|
|
for post <- author.posts do
|
|
|
|
assert post.author.id == author.id
|
|
|
|
end
|
2024-05-30 04:58:52 +12:00
|
|
|
|
|
|
|
post = Enum.at(author.posts, 0)
|
|
|
|
post = Map.update!(post, :author, &Map.put(&1, :name, "shouldn't change"))
|
|
|
|
|
|
|
|
assert post.author.name == "shouldn't change"
|
|
|
|
author_after_load =
|
|
|
|
post |> Ash.load!(:author, authorize?: false, lazy?: true) |> Map.get(:author)
|
|
|
|
|
|
|
|
assert author_after_load.name == "shouldn't change"
|
2022-05-19 05:11:30 +12:00
|
|
|
end
|
|
|
|
|
2023-06-15 11:51:50 +12:00
|
|
|
test "nested lazy loads work" do
|
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
post1 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2023-06-15 11:51:50 +12:00
|
|
|
|
2023-06-23 02:37:25 +12:00
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(:posts)
|
|
|
|
|> Ash.Query.filter(posts.id == ^post1.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read_one!(authorize?: true)
|
|
|
|
|> Ash.load!([posts: :author], lazy?: true)
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
author_id = author.id
|
|
|
|
|
|
|
|
assert %{posts: [%{author: %{id: ^author_id}}, %{author: %{id: ^author_id}}]} = author
|
|
|
|
|
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:author)
|
|
|
|
|> Ash.Query.filter(id == ^post1.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read_one!()
|
|
|
|
|> Ash.load!([author: :posts], lazy?: true)
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
assert %{author: %{posts: [_, _]}} = post
|
|
|
|
end
|
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
test "it allows loading across domains" do
|
2022-04-04 17:48:37 +12:00
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2022-04-04 17:48:37 +12:00
|
|
|
|
|
|
|
post =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2022-04-04 17:48:37 +12:00
|
|
|
|
|
|
|
rating =
|
|
|
|
Rating
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{rating: 10})
|
|
|
|
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
|
|
|
|
|> Domain2.create!()
|
2022-04-04 17:48:37 +12:00
|
|
|
|
|
|
|
assert [loaded_rating] =
|
|
|
|
author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.load!(posts: :ratings)
|
2022-04-04 17:48:37 +12:00
|
|
|
|> Map.get(:posts)
|
|
|
|
|> Enum.at(0)
|
|
|
|
|> Map.get(:ratings)
|
|
|
|
|
|
|
|
assert loaded_rating.id == rating.id
|
|
|
|
end
|
|
|
|
|
2021-05-10 09:24:48 +12:00
|
|
|
test "it allows loading many to many relationships" do
|
2020-07-12 18:25:53 +12:00
|
|
|
category1 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "lame"})
|
|
|
|
|> Ash.create!()
|
2020-07-12 18:25:53 +12:00
|
|
|
|
|
|
|
category2 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "cool"})
|
|
|
|
|> Ash.create!()
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
post =
|
2020-07-12 18:25:53 +12:00
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, [category1, category2],
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
[post] =
|
|
|
|
Post
|
2020-08-26 16:16:08 +12:00
|
|
|
|> Ash.Query.load(:categories)
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(id == ^post.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
assert [%{id: id1}, %{id: id2}] = post.categories
|
|
|
|
|
|
|
|
assert Enum.sort([category1.id, category2.id]) == Enum.sort([id1, id2])
|
|
|
|
end
|
|
|
|
|
2024-05-04 08:56:18 +12:00
|
|
|
test "it allows loading filtered many to many relationships with lateral joins" do
|
|
|
|
category1 =
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "1"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
category2 =
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "2"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
category3 =
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "3"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
category4 =
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "4"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(
|
|
|
|
:categories,
|
|
|
|
[category1, category2, category3, category4],
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
category_query =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.filter(name > "1")
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.limit(2)
|
|
|
|
|
|
|
|
[post] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(categories: category_query)
|
|
|
|
|> Ash.Query.filter(id == ^post.id)
|
|
|
|
|> Ash.read!(authorize?: true)
|
|
|
|
|
|
|
|
assert [%{id: id1}, %{id: id2}] = post.categories
|
|
|
|
|
|
|
|
assert [category2.id, category3.id] == [id1, id2]
|
|
|
|
end
|
|
|
|
|
2023-06-15 11:51:50 +12:00
|
|
|
test "it allows loading nested many to many relationships lazily" do
|
|
|
|
category1 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "lame"})
|
|
|
|
|> Ash.create!()
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
category2 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "cool"})
|
|
|
|
|> Ash.create!()
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
post =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, [category1, category2],
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2023-06-15 11:51:50 +12:00
|
|
|
|
2023-06-23 02:37:25 +12:00
|
|
|
assert [_] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(:categories)
|
|
|
|
|> Ash.Query.filter(id == ^post.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2023-06-15 11:51:50 +12:00
|
|
|
|
|
|
|
assert %{posts: [%{categories: [_, _]}]} =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.filter(id == ^category1.id)
|
|
|
|
|> Ash.Query.load(:posts)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read_one!()
|
|
|
|
|> Ash.load!([posts: :categories], lazy?: true)
|
2023-06-15 11:51:50 +12:00
|
|
|
end
|
|
|
|
|
2022-10-29 04:18:01 +13:00
|
|
|
test "it allows loading many to many relationships after the fact" do
|
|
|
|
category1 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "lame"})
|
|
|
|
|> Ash.create!()
|
2022-10-29 04:18:01 +13:00
|
|
|
|
|
|
|
category2 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "cool"})
|
|
|
|
|> Ash.create!()
|
2022-10-29 04:18:01 +13:00
|
|
|
|
|
|
|
post =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, [category1, category2],
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2022-10-29 04:18:01 +13:00
|
|
|
|
|
|
|
post = Post.get_by_id!(post.id, load: [:categories])
|
|
|
|
|
|
|
|
assert [%{id: id1}, %{id: id2}] = post.categories
|
|
|
|
|
|
|
|
assert Enum.sort([category1.id, category2.id]) == Enum.sort([id1, id2])
|
|
|
|
end
|
|
|
|
|
2022-11-21 19:37:58 +13:00
|
|
|
test "it produces a nice error message on loading invalid loads" do
|
|
|
|
assert_raise Ash.Error.Invalid, ~r/:non_existent_thing is not a valid load/, fn ->
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(categories: [posts: :non_existent_thing])
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2022-11-21 19:37:58 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-05-10 09:24:48 +12:00
|
|
|
test "it allows loading nested many to many relationships" do
|
2020-07-12 18:25:53 +12:00
|
|
|
category1 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "lame"})
|
|
|
|
|> Ash.create!()
|
2020-07-12 18:25:53 +12:00
|
|
|
|
|
|
|
category2 =
|
|
|
|
Category
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "cool"})
|
|
|
|
|> Ash.create!()
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
post =
|
2020-07-12 18:25:53 +12:00
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, [category1, category2],
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
[post] =
|
|
|
|
Post
|
2020-08-26 16:16:08 +12:00
|
|
|
|> Ash.Query.load(categories: :posts)
|
2020-10-08 18:22:55 +13:00
|
|
|
|> Ash.Query.filter(id == ^post.id)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(authorize?: true)
|
2020-06-22 15:26:47 +12:00
|
|
|
|
|
|
|
post_id = post.id
|
|
|
|
|
|
|
|
assert [%{posts: [%{id: ^post_id}]}, %{posts: [%{id: ^post_id}]}] = post.categories
|
|
|
|
end
|
2021-05-07 16:28:51 +12:00
|
|
|
|
|
|
|
test "it loads sorted relationships in the proper order" do
|
|
|
|
author =
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2021-05-07 16:28:51 +12:00
|
|
|
|
2024-01-19 18:05:42 +13:00
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2021-05-07 16:28:51 +12:00
|
|
|
|
|
|
|
post2 =
|
|
|
|
Post
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post2"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
2021-05-07 16:28:51 +12:00
|
|
|
|
|
|
|
[author] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(:latest_post)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!()
|
2021-05-07 16:28:51 +12:00
|
|
|
|
|
|
|
assert author.latest_post.id == post2.id
|
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|
2023-06-08 13:54:17 +12:00
|
|
|
|
2024-05-02 00:00:22 +12:00
|
|
|
test "it returns error with invalid keys" do
|
2024-05-02 00:58:39 +12:00
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "post1", category: "foo"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
assert_raise Ash.Error.Invalid, ~r/:invalid_key is not a valid load/, fn ->
|
|
|
|
post |> Ash.load!([:invalid_key])
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raise Ash.Error.Invalid, ~r/:invalid_key is not a valid load/, fn ->
|
|
|
|
Post.get_by_id!(post.id, load: [:invalid_key])
|
|
|
|
end
|
|
|
|
end
|
2024-05-02 00:00:22 +12:00
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
describe "loading through attributes" do
|
|
|
|
test "can load calculations through attributes" do
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(
|
|
|
|
:create,
|
|
|
|
%{name: "zerg", bio: %{first_name: "donald", last_name: "duck"}},
|
|
|
|
authorize?: false
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
assert [%{bio: %{full_name: "donald duck"}}] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(bio: :full_name)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!()
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
|
2023-06-23 02:37:25 +12:00
|
|
|
test "can load calculations through nil attributes" do
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "zerg"})
|
|
|
|
|> Ash.create!()
|
2023-06-23 02:37:25 +12:00
|
|
|
|
|
|
|
assert [%{bio: nil}] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(bio: :full_name)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!()
|
2023-06-23 02:37:25 +12:00
|
|
|
end
|
|
|
|
|
2023-06-08 13:54:17 +12:00
|
|
|
test "can load calculations through union" do
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(
|
|
|
|
:create,
|
|
|
|
%{name: "zerg", bio_union: %{type: "bio", first_name: "donald", last_name: "duck"}},
|
|
|
|
authorize?: false
|
|
|
|
)
|
|
|
|
|> Ash.create!(authorize?: false)
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(
|
|
|
|
:create,
|
|
|
|
%{
|
|
|
|
name: "zerg",
|
|
|
|
bio_union: %{type: "other_kind_of_bio", first_name: "donald", last_name: "duck"}
|
|
|
|
},
|
|
|
|
authorize?: false
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
assert [
|
|
|
|
%{bio_union: %Ash.Union{value: %{full_name: "donald duck"}}},
|
|
|
|
%{bio_union: %Ash.Union{value: %{full_name: "donald duck"}}}
|
|
|
|
] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(bio_union: [*: :full_name])
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!()
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
assert [
|
|
|
|
%{bio_union: %Ash.Union{value: %{full_name: "donald duck"}}},
|
|
|
|
%{bio_union: %Ash.Union{value: %{full_name: "donald duck"}}}
|
|
|
|
] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(bio_union: [bio: :full_name, other_kind_of_bio: :full_name])
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!()
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
test "can load calculations through union produced by a calculation" do
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(
|
|
|
|
:create,
|
|
|
|
%{
|
|
|
|
name: "zerg",
|
|
|
|
bio_union: %{type: "bio", first_name: "donald", last_name: "duck"}
|
|
|
|
},
|
|
|
|
authorize?: false
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
Author
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.Changeset.for_create(
|
|
|
|
:create,
|
|
|
|
%{
|
|
|
|
name: "zerg",
|
|
|
|
bio_union: %{type: "other_kind_of_bio", first_name: "donald", last_name: "duck"}
|
|
|
|
},
|
|
|
|
authorize?: false
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
2023-06-08 13:54:17 +12:00
|
|
|
|
|
|
|
assert [
|
2023-09-29 01:36:37 +13:00
|
|
|
%{
|
|
|
|
bio_union_calc: %Ash.Union{
|
2023-09-29 03:23:16 +13:00
|
|
|
value: %{
|
|
|
|
full_name: "donald duck",
|
|
|
|
forbidden_name: %Ash.ForbiddenField{},
|
|
|
|
forbidden_field: %Ash.ForbiddenField{}
|
|
|
|
}
|
2023-09-29 01:36:37 +13:00
|
|
|
}
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
bio_union_calc: %Ash.Union{
|
2023-09-29 03:23:16 +13:00
|
|
|
value: %{
|
|
|
|
full_name: "donald duck",
|
|
|
|
forbidden_name: %Ash.ForbiddenField{},
|
|
|
|
forbidden_field: %Ash.ForbiddenField{}
|
|
|
|
}
|
2023-09-29 01:36:37 +13:00
|
|
|
}
|
|
|
|
}
|
2023-06-08 13:54:17 +12:00
|
|
|
] =
|
|
|
|
Author
|
2023-09-29 01:36:37 +13:00
|
|
|
|> Ash.Query.load(bio_union_calc: {%{}, [*: [:full_name, :forbidden_name]]})
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.read!(actor: %{name: "zerg"}, authorize?: true)
|
2023-06-08 13:54:17 +12:00
|
|
|
end
|
|
|
|
end
|
2024-05-04 21:56:46 +12:00
|
|
|
|
|
|
|
describe "relationship pagination" do
|
|
|
|
test "it allows paginating has_many relationships with offset pagination" do
|
|
|
|
author1 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
author2 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
for i <- 0..9 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.page(limit: 2, offset: 2)
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|
|
|
|
assert [author1, author2] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [%{title: "author1 post2"}, %{title: "author1 post3"}]
|
|
|
|
} = author1.posts
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [%{title: "author2 post2"}, %{title: "author2 post3"}]
|
|
|
|
} = author2.posts
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [%{title: "author1 post4"}, %{title: "author1 post5"}]
|
|
|
|
} = Ash.page!(author1.posts, :next)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "it allows paginating has_many relationships with keyset pagination" do
|
|
|
|
author1 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
author2 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
for i <- 0..9 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.for_read(:keyset)
|
|
|
|
|> Ash.Query.page(limit: 2)
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|
|
|
|
assert [author1, author2] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [%{title: "author1 post0"}, %{title: "author1 post1"}]
|
|
|
|
} = author1.posts
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [%{title: "author2 post0"}, %{title: "author2 post1"}]
|
|
|
|
} = author2.posts
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [%{title: "author1 post2"}, %{title: "author1 post3"}]
|
|
|
|
} = Ash.page!(author1.posts, :next)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "it allows paginating many_to_many relationships with offset pagination" do
|
|
|
|
categories =
|
|
|
|
for i <- 0..9 do
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "category#{i}"})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
categories_0_to_6 = Enum.take(categories, 6)
|
|
|
|
categories_5_to_9 = Enum.slice(categories, 5..9)
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "a"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_0_to_6,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "b"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_5_to_9,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
paginated_categories =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.page(limit: 2, offset: 2)
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|
|
|
|
assert [post1, post2] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|> Ash.Query.load(categories: paginated_categories)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [%{name: "category2"}, %{name: "category3"}]
|
|
|
|
} = post1.categories
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [%{name: "category7"}, %{name: "category8"}]
|
|
|
|
} = post2.categories
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [%{name: "category4"}, %{name: "category5"}]
|
|
|
|
} = Ash.page!(post1.categories, :next)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "it allows paginating many_to_many relationships with keyset pagination" do
|
|
|
|
categories =
|
|
|
|
for i <- 0..9 do
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "category#{i}"})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
categories_0_to_6 = Enum.take(categories, 6)
|
|
|
|
categories_5_to_9 = Enum.slice(categories, 5..9)
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "a"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_0_to_6,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "b"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_5_to_9,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
paginated_categories =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.for_read(:keyset)
|
|
|
|
|> Ash.Query.page(limit: 2)
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|
|
|
|
assert [post1, post2] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|> Ash.Query.load(categories: paginated_categories)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [%{name: "category0"}, %{name: "category1"}]
|
|
|
|
} = post1.categories
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [%{name: "category5"}, %{name: "category6"}]
|
|
|
|
} = post2.categories
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [%{name: "category2"}, %{name: "category3"}]
|
|
|
|
} = Ash.page!(post1.categories, :next)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "works when nested with offset" do
|
|
|
|
author1 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
author2 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
categories =
|
|
|
|
for i <- 0..9 do
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "category#{i}"})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
categories_0_to_6 = Enum.take(categories, 6)
|
|
|
|
categories_5_to_9 = Enum.slice(categories, 5..9)
|
|
|
|
|
|
|
|
for i <- 0..5 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_0_to_6,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_5_to_9,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_categories =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.page(limit: 1, offset: 2)
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(categories: paginated_categories)
|
|
|
|
|> Ash.Query.page(limit: 1, offset: 1)
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|
|
|
|
assert [author1, _author2] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{
|
|
|
|
results: [
|
|
|
|
%{
|
|
|
|
title: "author1 post1",
|
|
|
|
categories: %Ash.Page.Offset{results: [%{name: "category2"}]}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
} = author1.posts
|
|
|
|
end
|
|
|
|
|
|
|
|
test "works when nested with keyset" do
|
|
|
|
author1 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
author2 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
categories =
|
|
|
|
for i <- 0..9 do
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "category#{i}"})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
categories_0_to_6 = Enum.take(categories, 6)
|
|
|
|
categories_5_to_9 = Enum.slice(categories, 5..9)
|
|
|
|
|
|
|
|
for i <- 0..5 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_0_to_6,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_5_to_9,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_categories =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.for_read(:keyset)
|
|
|
|
|> Ash.Query.page(limit: 1)
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.for_read(:keyset)
|
|
|
|
|> Ash.Query.load(categories: paginated_categories)
|
|
|
|
|> Ash.Query.page(limit: 1)
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|
|
|
|
assert [author1, _author2] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Keyset{
|
|
|
|
results: [
|
|
|
|
%{
|
|
|
|
title: "author1 post0",
|
|
|
|
categories: %Ash.Page.Keyset{results: [%{name: "category0"}]}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
} = author1.posts
|
|
|
|
end
|
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
test "doesn't honor required? pagination to maintain backwards compatibility" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "b"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
2024-05-04 21:56:46 +12:00
|
|
|
|> Ash.create!()
|
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
posts =
|
2024-05-04 21:56:46 +12:00
|
|
|
Post
|
2024-05-22 00:48:11 +12:00
|
|
|
|> Ash.Query.for_read(:required_pagination)
|
2024-05-04 21:56:46 +12:00
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
assert [_post] =
|
2024-05-04 21:56:46 +12:00
|
|
|
Author
|
|
|
|
|> Ash.read!()
|
2024-05-22 00:48:11 +12:00
|
|
|
|> Ash.load!(posts: posts)
|
2024-05-04 21:56:46 +12:00
|
|
|
end
|
2024-05-11 03:48:10 +12:00
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
test "it allows counting has_many relationships" do
|
|
|
|
author1 =
|
2024-05-11 03:48:10 +12:00
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
author2 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
for i <- 1..3 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
for i <- 1..6 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author2 post#{i}", author_id: author2.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.page(limit: 2, offset: 2, count: true)
|
|
|
|
|
|
|
|
assert [author1, author2] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{count: 3} = author1.posts
|
|
|
|
assert %Ash.Page.Offset{count: 6} = author2.posts
|
|
|
|
end
|
|
|
|
|
|
|
|
test "it allows counting many_to_many relationships" do
|
|
|
|
categories =
|
|
|
|
for i <- 1..9 do
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "category#{i}"})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
categories_1_to_3 = Enum.take(categories, 3)
|
|
|
|
categories_4_to_9 = Enum.slice(categories, 3..9)
|
|
|
|
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "a"})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_1_to_3,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
|
|
|
|> Ash.create!()
|
|
|
|
|
2024-05-11 03:48:10 +12:00
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "b"})
|
2024-05-22 00:48:11 +12:00
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories_4_to_9,
|
|
|
|
type: :append_and_remove
|
|
|
|
)
|
2024-05-11 03:48:10 +12:00
|
|
|
|> Ash.create!()
|
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
paginated_categories =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.page(limit: 2, count: true)
|
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|
|
|
|
assert [post1, post2] =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.sort(:title)
|
|
|
|
|> Ash.Query.load(categories: paginated_categories)
|
|
|
|
|> Ash.read!()
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{count: 3} = post1.categories
|
|
|
|
assert %Ash.Page.Offset{count: 6} = post2.categories
|
|
|
|
end
|
|
|
|
|
|
|
|
test "allows counting nested relationships" do
|
|
|
|
author1 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
_author2 =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "b"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
categories =
|
|
|
|
for i <- 1..3 do
|
|
|
|
Category
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "category#{i}"})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
for i <- 1..5 do
|
2024-05-11 03:48:10 +12:00
|
|
|
Post
|
2024-05-22 00:48:11 +12:00
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author1.id})
|
|
|
|
|> Ash.Changeset.manage_relationship(:categories, categories, type: :append_and_remove)
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
2024-05-11 03:48:10 +12:00
|
|
|
|
2024-05-22 00:48:11 +12:00
|
|
|
paginated_categories =
|
|
|
|
Category
|
|
|
|
|> Ash.Query.page(limit: 1, count: true)
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.load(categories: paginated_categories)
|
|
|
|
|> Ash.Query.page(limit: 1, count: true)
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{results: [author1], count: 2} =
|
2024-05-11 03:48:10 +12:00
|
|
|
Author
|
2024-05-22 00:48:11 +12:00
|
|
|
|> Ash.Query.sort(:name)
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
|
|
|
|> Ash.read!(page: [limit: 1, count: true])
|
|
|
|
|
|
|
|
assert %Ash.Page.Offset{count: 5, results: [%{categories: %Ash.Page.Offset{count: 3}}]} =
|
|
|
|
author1.posts
|
|
|
|
end
|
|
|
|
|
|
|
|
test "doesn't leak the internal count aggregate when counting" do
|
|
|
|
author =
|
|
|
|
Author
|
|
|
|
|> Ash.Changeset.for_create(:create, %{name: "a"})
|
|
|
|
|> Ash.create!()
|
|
|
|
|
|
|
|
for i <- 1..3 do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, %{title: "author1 post#{i}", author_id: author.id})
|
|
|
|
|> Ash.create!()
|
|
|
|
end
|
|
|
|
|
|
|
|
paginated_posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Query.page(limit: 2, offset: 2, count: true)
|
|
|
|
|
|
|
|
assert [author] =
|
|
|
|
Author
|
|
|
|
|> Ash.Query.load(posts: paginated_posts)
|
2024-05-11 03:48:10 +12:00
|
|
|
|> Ash.read!()
|
2024-05-22 00:48:11 +12:00
|
|
|
|
|
|
|
assert %Ash.Page.Offset{count: 3} = author.posts
|
|
|
|
assert %{} == author.aggregates
|
2024-05-11 03:48:10 +12:00
|
|
|
end
|
2024-05-04 21:56:46 +12:00
|
|
|
end
|
2019-12-12 11:45:59 +13:00
|
|
|
end
|