Repository: jeregrine/jsonapi
Pull request #124 (2018-08-03 19:20:23)

Omit non-Object meta
The serializer was including `meta` even in cases where its value would not be an Object, which is not in compliance with the JSONAPI spec (http://jsonapi.org/format/#document-meta). This PR makes the serializer omit the `meta` when `is_map(meta)` is `false`.

I also DRYed up the tests, since my added ones re-used some of the existing default data.

mjc: I think this is a bugfix rather than an enhancement as `"meta": null` was the previous output and that is not allowed by the spec.
doomspork: Thank you and welcome @kbaird 😁 

I've corrected the label

Patch:
diff --git a/lib/jsonapi/query_parser.ex b/lib/jsonapi/query_parser.ex
index 4b04b45..caef14b 100644
--- a/lib/jsonapi/query_parser.ex
+++ b/lib/jsonapi/query_parser.ex
@@ -117,8 +117,7 @@ defmodule JSONAPI.QueryParser do
       requested_fields =
         value
         |> String.split(",")
-        |> Enum.map(&String.to_atom/1)
-        |> Enum.into(MapSet.new())
+        |> Enum.into(MapSet.new(), &String.to_atom/1)
 
       unless MapSet.subset?(requested_fields, valid_fields) do
         bad_fields =
diff --git a/lib/jsonapi/serializer.ex b/lib/jsonapi/serializer.ex
index fc80b44..bced474 100644
--- a/lib/jsonapi/serializer.ex
+++ b/lib/jsonapi/serializer.ex
@@ -25,10 +25,16 @@ defmodule JSONAPI.Serializer do
 
     encoded_data = %{
       data: encoded_data,
-      included: flatten_included(to_include),
-      meta: meta
+      included: flatten_included(to_include)
     }
 
+    encoded_data =
+      if is_map(meta) do
+        Map.put(encoded_data, :meta, meta)
+      else
+        encoded_data
+      end
+
     merge_links(encoded_data, data, view, conn, remove_links?(), with_pagination?())
   end
 
diff --git a/lib/jsonapi/utils/underscore.ex b/lib/jsonapi/utils/underscore.ex
index be109cc..3d2c2ff 100644
--- a/lib/jsonapi/utils/underscore.ex
+++ b/lib/jsonapi/utils/underscore.ex
@@ -24,9 +24,7 @@ defmodule JSONAPI.Utils.Underscore do
   end
 
   def underscore(value) when is_map(value) do
-    value
-    |> Enum.map(&underscore/1)
-    |> Enum.into(%{})
+    Enum.into(value, %{}, &underscore/1)
   end
 
   def underscore({key, value}) do


Test patch:
diff --git a/test/jsonapi_test.exs b/test/jsonapi_test.exs
index 75394fd..a713295 100644
--- a/test/jsonapi_test.exs
+++ b/test/jsonapi_test.exs
@@ -2,6 +2,14 @@ defmodule JSONAPITest do
   use ExUnit.Case
   use Plug.Test
 
+  @default_data %{
+    id: 1,
+    text: "Hello",
+    body: "Hi",
+    author: %{username: "jason", id: 2},
+    other_user: %{username: "josh", id: 3}
+  }
+
   defmodule PostView do
     use JSONAPI.View
 
@@ -83,15 +91,7 @@ defmodule JSONAPITest do
     conn =
       :get
       |> conn("/posts")
-      |> Plug.Conn.assign(:data, [
-        %{
-          id: 1,
-          text: "Hello",
-          body: "Hi",
-          author: %{username: "jason", id: 2},
-          other_user: %{username: "josh", id: 3}
-        }
-      ])
+      |> Plug.Conn.assign(:data, [@default_data])
       |> Plug.Conn.assign(:meta, %{total_pages: 1})
       |> MyPostPlug.call([])
 
@@ -133,15 +133,7 @@ defmodule JSONAPITest do
   test "handles includes properly" do
     conn =
       conn(:get, "/posts?include=other_user")
-      |> Plug.Conn.assign(:data, [
-        %{
-          id: 1,
-          text: "Hello",
-          body: "Hi",
-          author: %{username: "jason", id: 2},
-          other_user: %{username: "josh", id: 3}
-        }
-      ])
+      |> Plug.Conn.assign(:data, [@default_data])
       |> Plug.Conn.fetch_query_params()
       |> MyPostPlug.call([])
 
@@ -272,4 +264,29 @@ defmodule JSONAPITest do
 
     assert Map.has_key?(json, "links")
   end
+
+  test "omits explicit nil meta values as per http://jsonapi.org/format/#document-meta" do
+    conn =
+      :get
+      |> conn("/posts")
+      |> Plug.Conn.assign(:data, [@default_data])
+      |> Plug.Conn.assign(:meta, nil)
+      |> MyPostPlug.call([])
+
+    json = conn.resp_body |> Jason.decode!()
+
+    refute Map.has_key?(json, "meta")
+  end
+
+  test "omits implicit nil meta values as per http://jsonapi.org/format/#document-meta" do
+    conn =
+      :get
+      |> conn("/posts")
+      |> Plug.Conn.assign(:data, [@default_data])
+      |> MyPostPlug.call([])
+
+    json = conn.resp_body |> Jason.decode!()
+
+    refute Map.has_key?(json, "meta")
+  end
 end