Skip to content

Commit bd36feb

Browse files
authored
improvement: add always_include_linkage param to resources (#362)
1 parent d827326 commit bd36feb

File tree

7 files changed

+320
-19
lines changed

7 files changed

+320
-19
lines changed

.formatter.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
spark_locals_without_parens = [
2+
always_include_linkage: 1,
23
authorize?: 1,
34
base: 1,
45
base_route: 1,

documentation/dsls/DSL-AshJsonApi.Resource.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ end
6262
| Name | Type | Default | Docs |
6363
|------|------|---------|------|
6464
| [`type`](#json_api-type){: #json_api-type } | `String.t` | | The resource identifier type of this resource in JSON:API |
65+
| [`always_include_linkage`](#json_api-always_include_linkage){: #json_api-always_include_linkage } | `list(atom)` | `[]` | A list of relationships that should always have their linkage included in the resource |
6566
| [`includes`](#json_api-includes){: #json_api-includes } | `any \| list(any)` | `[]` | A keyword list of all paths that are includable from this resource |
6667
| [`include_nil_values?`](#json_api-include_nil_values?){: #json_api-include_nil_values? } | `any` | | Whether or not to include properties for values that are nil in the JSON output |
6768
| [`default_fields`](#json_api-default_fields){: #json_api-default_fields } | `list(atom)` | | The fields to include in the object if the `fields` query parameter does not specify. Defaults to all public |

lib/ash_json_api/includes/includer.ex

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ defmodule AshJsonApi.Includes.Includer do
3838
includes_keyword
3939
|> Enum.reduce({preloaded, includes_map}, fn
4040
{relationship, further}, {preloaded_without_linkage, includes_map} ->
41+
linkage_only? =
42+
case further do
43+
%{__linkage_only__: true} -> true
44+
_ -> false
45+
end
46+
4147
{related, includes_map} =
4248
preloaded
4349
|> Enum.flat_map(fn record ->
@@ -61,22 +67,26 @@ defmodule AshJsonApi.Includes.Includer do
6167
)
6268

6369
includes_map =
64-
related
65-
|> List.wrap()
66-
|> Enum.reduce(includes_map, fn related_item, includes_map ->
67-
type = AshJsonApi.Resource.Info.type(related_item)
68-
id = AshJsonApi.Resource.encode_primary_key(related_item)
69-
70-
case Map.fetch(includes_map, {type, id}) do
71-
{:ok, _} ->
72-
Map.update!(includes_map, {type, id}, fn existing ->
73-
merge_linkages(existing, related_item)
74-
end)
75-
76-
:error ->
77-
Map.put(includes_map, {type, id}, Map.put_new(related_item, :__linkage__, %{}))
78-
end
79-
end)
70+
if linkage_only? do
71+
includes_map
72+
else
73+
related
74+
|> List.wrap()
75+
|> Enum.reduce(includes_map, fn related_item, includes_map ->
76+
type = AshJsonApi.Resource.Info.type(related_item)
77+
id = AshJsonApi.Resource.encode_primary_key(related_item)
78+
79+
case Map.fetch(includes_map, {type, id}) do
80+
{:ok, _} ->
81+
Map.update!(includes_map, {type, id}, fn existing ->
82+
merge_linkages(existing, related_item)
83+
end)
84+
85+
:error ->
86+
Map.put(includes_map, {type, id}, Map.put_new(related_item, :__linkage__, %{}))
87+
end
88+
end)
89+
end
8090

8191
{preloaded_with_linkage, includes_map}
8292
end)

lib/ash_json_api/request.ex

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,28 @@ defmodule AshJsonApi.Request do
680680
)
681681
end
682682

683-
defp set_include_queries(includes, fields, field_inputs, filters, sorts, resource, path \\ []) do
683+
defp set_include_queries(includes, fields, field_inputs, filters, sorts, resource, path \\ [])
684+
685+
defp set_include_queries(:linkage_only, _, _, _, _, _, _), do: []
686+
687+
defp set_include_queries(includes, fields, field_inputs, filters, sorts, resource, path) do
688+
includes =
689+
Enum.reduce(
690+
AshJsonApi.Resource.Info.always_include_linkage(resource),
691+
includes,
692+
fn key, includes ->
693+
if Keyword.has_key?(includes, key) do
694+
includes
695+
else
696+
Keyword.put(includes, key, :linkage_only)
697+
end
698+
end
699+
)
700+
684701
Enum.map(includes, fn {key, nested} ->
685702
related = public_related(resource, key)
686703

687-
nested =
704+
nested_queries =
688705
set_include_queries(nested, fields, field_inputs, filters, sorts, related, path ++ [key])
689706

690707
related_field_inputs = Map.get(field_inputs, related, %{})
@@ -704,12 +721,13 @@ defmodule AshJsonApi.Request do
704721
value -> {field, value}
705722
end
706723
end)
707-
|> Kernel.++(nested)
724+
|> Kernel.++(nested_queries)
708725

709726
new_query =
710727
related
711728
|> Ash.Query.new()
712729
|> Ash.Query.load(load)
730+
|> Map.put(:__linkage_only__, nested == :linkage_only)
713731

714732
filtered_query =
715733
case Map.fetch(filters, path ++ [key]) do

lib/ash_json_api/resource/info.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ defmodule AshJsonApi.Resource.Info do
1515
Extension.get_opt(resource, [:json_api], :derive_sort, true, false)
1616
end
1717

18+
def always_include_linkage(resource) do
19+
Extension.get_opt(resource, [:json_api], :always_include_linkage, [], false)
20+
end
21+
1822
def includes(resource) do
1923
Extension.get_opt(resource, [:json_api], :includes, [], false)
2024
end

lib/ash_json_api/resource/resource.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,12 @@ defmodule AshJsonApi.Resource do
494494
type: :string,
495495
doc: "The resource identifier type of this resource in JSON:API"
496496
],
497+
always_include_linkage: [
498+
type: {:list, :atom},
499+
doc:
500+
"A list of relationships that should always have their linkage included in the resource",
501+
default: []
502+
],
497503
includes: [
498504
type: {:wrap_list, :any},
499505
default: [],

0 commit comments

Comments
 (0)