Skip to content

Commit c9042fa

Browse files
authored
feat: make the serialization of nil relationships configurable (#403)
Enable serializing loaded nil associations serializable with the :serialize_nil_relationships config option
1 parent 3992ef8 commit c9042fa

3 files changed

Lines changed: 99 additions & 2 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ config :jsonapi,
199199
namespace: "/api",
200200
field_transformation: :underscore,
201201
remove_links: false,
202+
serialize_nil_relationships: false,
202203
json_library: Jason,
203204
paginator: nil
204205
```
@@ -219,6 +220,10 @@ config :jsonapi,
219220
`:camelize_shallow` or `:dasherize_shallow`.
220221
- **remove_links**. `links` data can optionally be removed from the payload via
221222
setting the configuration above to `true`. Defaults to `false`.
223+
- **serialize_nil_relationships**. By default, relationships on a resource that
224+
are `nil` will be omitted during serialization. Setting this to `true` will
225+
serialize these relationships, provided they are loaded on the resource.
226+
Defaults to `false`.
222227
- **json_library**. Defaults to [Jason](https://hex.pm/packages/jason).
223228
- **paginator**. Module implementing pagination links generation. Defaults to `nil`.
224229

lib/jsonapi/serializer.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ defmodule JSONAPI.Serializer do
270270
|> Enum.uniq()
271271
end
272272

273-
defp assoc_loaded?(nil), do: false
273+
defp assoc_loaded?(nil), do: serialize_nil_relationships?()
274274
defp assoc_loaded?(%{__struct__: Ecto.Association.NotLoaded}), do: false
275275
defp assoc_loaded?(_association), do: true
276276

@@ -304,6 +304,9 @@ defmodule JSONAPI.Serializer do
304304

305305
defp remove_links?, do: Application.get_env(:jsonapi, :remove_links, false)
306306

307+
@spec serialize_nil_relationships? :: boolean()
308+
defp serialize_nil_relationships?, do: Application.get_env(:jsonapi, :serialize_nil_relationships, false)
309+
307310
defp transform_fields(fields) do
308311
case Utils.String.field_transformation() do
309312
:camelize -> Utils.String.expand_fields(fields, &Utils.String.camelize/1)

test/jsonapi/serializer_test.exs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ defmodule JSONAPI.SerializerTest do
202202

203203
%PolymorphicDataTwo{} ->
204204
"polymorphic_data_one"
205+
206+
nil ->
207+
nil
205208
end
206209
end
207210

@@ -221,6 +224,7 @@ defmodule JSONAPI.SerializerTest do
221224

222225
on_exit(fn ->
223226
Application.delete_env(:jsonapi, :field_transformation)
227+
Application.delete_env(:jsonapi, :serialize_nil_relationships)
224228
end)
225229

226230
{:ok, []}
@@ -360,7 +364,7 @@ defmodule JSONAPI.SerializerTest do
360364
assert Enum.count(encoded[:included]) == 1
361365
end
362366

363-
test "serialize handles a nil relationship" do
367+
test "serialize excludes a nil relationship by default" do
364368
data = %{
365369
id: 1,
366370
text: "Hello",
@@ -380,11 +384,96 @@ defmodule JSONAPI.SerializerTest do
380384
assert attributes[:body] == data[:body]
381385

382386
assert encoded_data[:links][:self] == PostView.url_for(data, nil)
387+
refute Map.has_key?(encoded_data[:relationships], :best_comments)
388+
assert map_size(encoded_data[:relationships]) == 1
389+
390+
assert Enum.count(encoded[:included]) == 1
391+
end
392+
393+
test "serialize excludes unloaded ecto association by default" do
394+
data = %{
395+
id: 1,
396+
text: "Hello",
397+
body: "Hello world",
398+
author: %{id: 2, username: "jason"},
399+
best_comments: %{__struct__: Ecto.Association.NotLoaded}
400+
}
401+
402+
encoded = Serializer.serialize(PostView, data, nil)
403+
404+
encoded_data = encoded[:data]
405+
assert encoded_data[:id] == PostView.id(data)
406+
assert encoded_data[:type] == PostView.type()
407+
408+
attributes = encoded_data[:attributes]
409+
assert attributes[:text] == data[:text]
410+
assert attributes[:body] == data[:body]
411+
412+
assert encoded_data[:links][:self] == PostView.url_for(data, nil)
413+
refute Map.has_key?(encoded_data[:relationships], :best_comments)
383414
assert map_size(encoded_data[:relationships]) == 1
384415

385416
assert Enum.count(encoded[:included]) == 1
386417
end
387418

419+
test "serialize includes nil relationships when configured" do
420+
Application.put_env(:jsonapi, :serialize_nil_relationships, true)
421+
422+
data = %{
423+
id: 1,
424+
text: "Hello",
425+
body: "Hello world",
426+
author: %{id: 2, username: "jason"},
427+
best_comments: nil
428+
}
429+
430+
encoded = Serializer.serialize(PostView, data, nil)
431+
432+
encoded_data = encoded[:data]
433+
assert encoded_data[:id] == PostView.id(data)
434+
assert encoded_data[:type] == PostView.type()
435+
436+
attributes = encoded_data[:attributes]
437+
assert attributes[:text] == data[:text]
438+
assert attributes[:body] == data[:body]
439+
440+
assert encoded_data[:links][:self] == PostView.url_for(data, nil)
441+
assert map_size(encoded_data[:relationships]) == 3
442+
assert encoded_data[:relationships][:best_comments][:data] == nil
443+
assert encoded_data[:relationships][:polymorphics][:data] == nil
444+
445+
assert Enum.count(encoded[:included]) == 1
446+
end
447+
448+
test "serialize excludes unloaded ecto association when serialize nil relationships configured" do
449+
Application.put_env(:jsonapi, :serialize_nil_relationships, true)
450+
451+
data = %{
452+
id: 1,
453+
text: "Hello",
454+
body: "Hello world",
455+
author: %{id: 2, username: "jason"},
456+
best_comments: %{__struct__: Ecto.Association.NotLoaded}
457+
}
458+
459+
encoded = Serializer.serialize(PostView, data, nil)
460+
461+
encoded_data = encoded[:data]
462+
assert encoded_data[:id] == PostView.id(data)
463+
assert encoded_data[:type] == PostView.type()
464+
465+
attributes = encoded_data[:attributes]
466+
assert attributes[:text] == data[:text]
467+
assert attributes[:body] == data[:body]
468+
469+
assert encoded_data[:links][:self] == PostView.url_for(data, nil)
470+
assert map_size(encoded_data[:relationships]) == 2
471+
assert Map.has_key?(encoded_data[:relationships], :polymorphics)
472+
refute Map.has_key?(encoded_data[:relationships], :best_comments)
473+
474+
assert Enum.count(encoded[:included]) == 1
475+
end
476+
388477
test "serialize handles a relationship self link on a show request" do
389478
data = %{
390479
id: 1,

0 commit comments

Comments
 (0)