Repository: jeregrine/jsonapi (elixir)

Issue:
Links Usage Unclear
I've read over the docs and have done my best to grok the code, but I'm a bit confused on how to specify links.

Suppose I want to include a link for a Book's many Pages, I might want something like this:

```json
{
  "data": {
    "id": 123,
    "title": "Curious George"
  },
  "relationships": {
    "pages": {
      "self": "/api/book/123/relationship/pages"
// snip
```

Setting up my `BookView` isn't quite clear to me. If I override `links/2` it doesn't seem to do anything. Overriding `relationships/0` as so doesn't seem to work:

```elixir
def relationships do
  [pages: PageView]
end
```

I think I may be missing something obvious, but I could use a hand.

Pull request description:
Support Arbitrary User-Defined Links
JSON:API allows for a `links` object in the `data` payload (see
https://jsonapi.org/format/#document-links). Prior to this commit we
only included user-defined links should they be paginating. This commit
ensures these links are always included unless otherwise specified by
the `:remove_links` option.

Resolves #141

Patch:
diff --git a/lib/jsonapi/serializer.ex b/lib/jsonapi/serializer.ex
index 4c92d1d..ee81bc5 100644
--- a/lib/jsonapi/serializer.ex
+++ b/lib/jsonapi/serializer.ex
@@ -145,6 +145,12 @@ defmodule JSONAPI.Serializer do
     merge_related_links(data, info, remove_links?())
   end
 
+  defp merge_base_links(doc, data, view, conn) do
+    links = Map.merge(%{self: view.url_for(data, conn)}, view.links(data, conn))
+
+    Map.merge(doc, %{links: links})
+  end
+
   defp merge_links(doc, data, view, conn, false, true) do
     pagination_links = view.links(data, conn)
 
@@ -154,15 +160,11 @@ defmodule JSONAPI.Serializer do
       )
     end
 
-    links =
-      %{self: view.url_for(data, conn)}
-      |> Map.merge(view.links(data, conn))
-
-    Map.merge(doc, %{links: links})
+    merge_base_links(doc, data, view, conn)
   end
 
   defp merge_links(doc, data, view, conn, false, false) do
-    Map.merge(doc, %{links: %{self: view.url_for(data, conn)}})
+    merge_base_links(doc, data, view, conn)
   end
 
   defp merge_links(doc, _data, _view, _conn, _remove_links, _with_pagination), do: doc


Test patch:
diff --git a/test/serializer_test.exs b/test/serializer_test.exs
index 165e5fe..5aecd68 100644
--- a/test/serializer_test.exs
+++ b/test/serializer_test.exs
@@ -88,6 +88,26 @@ defmodule JSONAPISerializerTest do
     end
   end
 
+  defmodule ExpensiveResourceView do
+    use JSONAPI.View
+
+    def fields, do: [:name]
+
+    def type, do: "expensive-resource"
+
+    def links(data, _conn) do
+      %{
+        queue: "/expensive-resource/queue/#{data.id}",
+        promotions: %{
+          href: "/promotions?rel=#{data.id}",
+          meta: %{
+            title: "Stuff you might be interested in"
+          }
+        }
+      }
+    end
+  end
+
   setup do
     Application.put_env(:jsonapi, :field_transformation, :underscore)
 
@@ -549,4 +569,25 @@ defmodule JSONAPISerializerTest do
 
     refute encoded[:links][:next]
   end
+
+  test "serialize can include arbitrary, user-defined, links" do
+    data = %{id: 1}
+
+    assert %{
+             links: links
+           } = Serializer.serialize(ExpensiveResourceView, data, nil)
+
+    expected_links = %{
+      self: "/expensive-resource/#{data.id}",
+      queue: "/expensive-resource/queue/#{data.id}",
+      promotions: %{
+        href: "/promotions?rel=#{data.id}",
+        meta: %{
+          title: "Stuff you might be interested in"
+        }
+      }
+    }
+
+    assert expected_links == links
+  end
 end