-
-
Notifications
You must be signed in to change notification settings - Fork 206
/
validate_relationship_attributes_match.ex
103 lines (83 loc) · 2.93 KB
/
validate_relationship_attributes_match.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
101
102
103
defmodule Ash.Resource.Verifiers.ValidateRelationshipAttributesMatch do
@moduledoc """
Shows a warning on potentially incompatible relationship attributes.
"""
use Spark.Dsl.Verifier
def verify(dsl) do
module = Spark.Dsl.Verifier.get_persisted(dsl, :module)
dsl
|> Ash.Resource.Info.relationships()
|> Enum.reject(&(&1.type == :many_to_many))
|> Enum.flat_map(fn
%{
source_attribute: source_attribute,
destination_attribute: destination_attribute,
destination: destination,
validate_destination_attribute?: true
} = rel ->
case Code.ensure_compiled(destination) do
{:module, destination} ->
[{rel, source_attribute, destination_attribute, destination}]
_ ->
# older versions of ash/spark/elixir can get here, but not newer ones
[]
end
_ ->
[]
end)
|> Enum.flat_map(fn {rel, source, dest, resource} ->
source = Ash.Resource.Info.attribute(dsl, source)
dest = Ash.Resource.Info.attribute(resource, dest)
if source && dest && !compatible_types?(source, dest) do
[warning(rel, module, source, dest, resource)]
else
[]
end
end)
|> case do
[] ->
:ok
warnings ->
{:warn, warnings}
end
end
defp warning(rel, module, source, dest, resource) do
"""
Source attribute may not be compatible with destination attribute for #{inspect(module)}.#{rel.name} to #{inspect(resource)}
`source_attribute` type: `#{inspect(source.type)}`
`destination_attribute` type: `#{inspect(dest.type)}`
Types are considered compatible if:
1. They are exactly the same
2. Their `storage_type/0` callback returns the same value
3. The storage types are `:text` and `:string`
4. The relationship has `validate_destination_attribute?` set to `false`.
5. They are explicitly configured as compatible. To do so in this instance, add it to your config like so:
config :ash, :compatible_foreign_key_types,
[
{#{inspect(source.type)}, #{inspect(dest.type)}}
]
"""
end
defp compatible_types?(%{type: source}, %{type: dest}) do
left_storage_type = Ash.Type.storage_type(source)
right_storage_type = Ash.Type.storage_type(dest)
cond do
source == dest ->
true
left_storage_type == right_storage_type ->
true
left_storage_type == :text and right_storage_type == :string ->
true
left_storage_type == :string and right_storage_type == :text ->
true
true ->
configured_as_compatible?(source, dest)
end
end
@compatible_foreign_key_types Application.compile_env(:ash, :compatible_foreign_key_types, [])
defp configured_as_compatible?(source, dest) do
Enum.find(@compatible_foreign_key_types, fn {left, right} ->
(source == left and dest == right) or (source == right and dest == left)
end)
end
end