/
validate_related_resource_inclusion.ex
100 lines (85 loc) · 3.43 KB
/
validate_related_resource_inclusion.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
defmodule Ash.Registry.ResourceValidations.Verifiers.ValidateRelatedResourceInclusion do
@moduledoc """
Ensures that all related resources are included in an API.
"""
use Spark.Dsl.Verifier
alias Spark.Dsl.Verifier
@impl true
def verify(dsl) do
resources =
dsl
|> Verifier.get_entities([:entries])
|> Enum.map(& &1.entry)
for resource <- resources do
for relationship <- Ash.Resource.Info.relationships(resource) do
if relationship.api && !Spark.Dsl.is?(relationship.api, Ash.Api) do
raise """
Api #{inspect(relationship.api)} referenced by #{inspect(resource)}.#{relationship.name} is not a valid api
"""
end
message =
if relationship.api do
"is not accepted by api `#{inspect(relationship.api)}`"
else
"is not in registry `#{inspect(Verifier.get_persisted(dsl, :module))}`"
end
unless resource_accepted?(
relationship.api,
relationship.destination,
Verifier.get_persisted(dsl, :module),
resources
) do
if relationship.type == :has_many && relationship.autogenerated_join_relationship_of do
parent_relationship =
Ash.Resource.Info.relationship(
resource,
relationship.autogenerated_join_relationship_of
)
raise """
Resource `#{inspect(relationship.destination)}` #{message} for autogenerated join relationship: `:#{relationship.name}`
Relationship was generated by the `many_to_many` relationship `#{inspect(parent_relationship.name)}`
If the `through` resource `#{inspect(relationship.destination)}` is not accepted by the same
api as the destination resource `#{inspect(parent_relationship.destination)}`,
then you must define that relationship manually. To define it manually, add the following to your
relationships:
has_many :#{relationship.name}, #{inspect(relationship.destination)} do
# configure the relationship attributes
...
end
You can use a name other than `:#{relationship.name}`, but if you do, make sure to
add that to `:#{parent_relationship.name}`, i.e
many_to_many :#{relationship.name}, #{inspect(relationship.destination)} do
...
join_relationship_name :your_new_name
end
"""
else
raise """
Resource `#{inspect(relationship.destination)}` in relationship `:#{relationship.name}` #{message}. Please do one of the following
1. add the resource to the registry `#{inspect(registry(relationship, dsl))}`
2. configure a different api
"""
end
end
end
end
:ok
end
defp registry(relationship, dsl) do
if relationship.api do
Ash.Api.Info.registry(relationship.api)
else
Verifier.get_persisted(dsl, :module)
end
end
defp resource_accepted?(nil, destination, _registry, resources) do
destination in resources
end
defp resource_accepted?(api, destination, registry_module, resources) do
if Ash.Api.Info.registry(api) == registry_module do
destination in resources
else
match?({:ok, _}, Ash.Api.Info.resource(api, destination))
end
end
end