Skip to content

Commit 35cddec

Browse files
committed
field via aliased relationship calculation
1 parent 6f766f6 commit 35cddec

3 files changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule Diffo.Provider.Calculations.FieldViaAliasedRelationship do
6+
@moduledoc false
7+
use Ash.Resource.Calculation
8+
9+
@impl true
10+
def load(_query, _opts, _context), do: []
11+
12+
@impl true
13+
def calculate(records, opts, _context) do
14+
alias_name = opts[:alias]
15+
field = opts[:field]
16+
17+
Enum.map(records, fn record ->
18+
record.id
19+
|> traverse(alias_name)
20+
|> Enum.flat_map(fn target_id ->
21+
Diffo.Provider.Instance
22+
|> Ash.Query.filter_input(id: target_id)
23+
|> Ash.read!(domain: Diffo.Provider)
24+
|> Enum.map(&Map.get(&1, field))
25+
end)
26+
end)
27+
end
28+
29+
defp traverse(id, nil) do
30+
Diffo.Provider.DefinedSimpleRelationship
31+
|> Ash.Query.filter_input(source_id: id)
32+
|> Ash.read!(domain: Diffo.Provider)
33+
|> Enum.map(& &1.target_id)
34+
end
35+
36+
defp traverse(id, alias_name) do
37+
Diffo.Provider.DefinedSimpleRelationship
38+
|> Ash.Query.filter_input(source_id: id, alias: alias_name)
39+
|> Ash.read!(domain: Diffo.Provider)
40+
|> Enum.map(& &1.target_id)
41+
end
42+
end
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule Diffo.Provider.Extension.FieldViaAliasedRelationshipTest do
6+
@moduledoc false
7+
use ExUnit.Case, async: true
8+
@moduletag :domain_extended
9+
10+
alias Diffo.Test.Parties
11+
alias Diffo.Test.Servo
12+
13+
setup do
14+
AshNeo4j.Sandbox.checkout()
15+
on_exit(&AshNeo4j.Sandbox.rollback/0)
16+
end
17+
18+
describe "FieldViaAliasedRelationship — aliased" do
19+
test "returns field from target instance reached via alias" do
20+
{:ok, shelf} = Parties.build_shelf_with_installer()
21+
{:ok, card} = Servo.build_card(%{name: "target-card"})
22+
23+
Diffo.Provider.create_defined_simple_relationship!(%{
24+
type: :assignedTo,
25+
alias: :link,
26+
source_id: shelf.id,
27+
target_id: card.id
28+
})
29+
30+
shelf = Ash.load!(shelf, [:linked_target_name], domain: Servo)
31+
32+
assert shelf.linked_target_name == ["target-card"]
33+
end
34+
35+
test "returns empty list when no matching relationship exists" do
36+
{:ok, shelf} = Parties.build_shelf_with_installer()
37+
38+
shelf = Ash.load!(shelf, [:linked_target_name], domain: Servo)
39+
40+
assert shelf.linked_target_name == []
41+
end
42+
43+
test "alias filters to only the matching target" do
44+
{:ok, shelf} = Parties.build_shelf_with_installer()
45+
{:ok, card_a} = Servo.build_card(%{name: "target-a"})
46+
{:ok, card_b} = Servo.build_card(%{name: "target-b"})
47+
48+
Diffo.Provider.create_defined_simple_relationship!(%{
49+
type: :assignedTo,
50+
alias: :link,
51+
source_id: shelf.id,
52+
target_id: card_a.id
53+
})
54+
55+
Diffo.Provider.create_defined_simple_relationship!(%{
56+
type: :assignedTo,
57+
alias: :other,
58+
source_id: shelf.id,
59+
target_id: card_b.id
60+
})
61+
62+
shelf = Ash.load!(shelf, [:linked_target_name], domain: Servo)
63+
64+
assert shelf.linked_target_name == ["target-a"]
65+
end
66+
end
67+
68+
describe "FieldViaAliasedRelationship — unaliased (all targets)" do
69+
test "returns fields from all related target instances regardless of alias" do
70+
{:ok, shelf} = Parties.build_shelf_with_installer()
71+
{:ok, card_a} = Servo.build_card(%{name: "target-a"})
72+
{:ok, card_b} = Servo.build_card(%{name: "target-b"})
73+
74+
Diffo.Provider.create_defined_simple_relationship!(%{
75+
type: :assignedTo,
76+
alias: :link,
77+
source_id: shelf.id,
78+
target_id: card_a.id
79+
})
80+
81+
Diffo.Provider.create_defined_simple_relationship!(%{
82+
type: :assignedTo,
83+
alias: :other,
84+
source_id: shelf.id,
85+
target_id: card_b.id
86+
})
87+
88+
shelf = Ash.load!(shelf, [:all_linked_names], domain: Servo)
89+
90+
assert Enum.sort(shelf.all_linked_names) == ["target-a", "target-b"]
91+
end
92+
93+
test "returns empty list when no relationships exist" do
94+
{:ok, shelf} = Parties.build_shelf_with_installer()
95+
96+
shelf = Ash.load!(shelf, [:all_linked_names], domain: Servo)
97+
98+
assert shelf.all_linked_names == []
99+
end
100+
end
101+
end

test/support/resource/instance/shelf_instance.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ defmodule Diffo.Test.Instance.ShelfInstance do
8484
end
8585
end
8686

87+
calculations do
88+
calculate :linked_target_name, {:array, :string},
89+
{Diffo.Provider.Calculations.FieldViaAliasedRelationship, [alias: :link, field: :name]}
90+
91+
calculate :all_linked_names, {:array, :string},
92+
{Diffo.Provider.Calculations.FieldViaAliasedRelationship, [field: :name]}
93+
end
94+
8795
actions do
8896
create :build do
8997
description "creates a new Shelf resource instance for build"

0 commit comments

Comments
 (0)