forked from rails-api/active_model_serializers
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support JSONAPI include params with links/loading
The current implementation does support conditionally sideloading relationships based on the 'include' URL param. However, omitting the relationship still loads the relationship (to populate the type/id 'relationships' payload), somewhat defeating the purpose. Instead, this changes the flow to: 1. If the relationship is included, load it and include it the response. 2. If the relationship is not included but there is a JSONAPI link, include the link in the response but do not load the relationship or include data. 3. If the relationship is not in the URL param and there is no link, do not include this node in the 'relationships' response. The `current_include_tree` edits in json_api.rb are to pass the current nested includes. This is to support when multiple entities have the same relationship, e.g. `/blogs/?include=posts.tags,tags` should include both blog tags and post tags, but `/blogs/?include=posts.tags` should only include post tags. This API is opt-in to support users who always want to load `relationships` data. To opt-in: ```ruby class BlogSerializer < ActiveModel::Serializer associations_via_include_param(true) # opt-in to this pattern has_many :tags has_many :posts do link :self, '//example.com/blogs/relationships/posts' end end ``` JSONAPI include parameters (http://jsonapi.org/format/#fetching-includes). Fixes rails-api#1707 Fixes rails-api#1555
- Loading branch information
Lee Richmond
committed
May 9, 2016
1 parent
2b49899
commit 1664fbc
Showing
6 changed files
with
212 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
require 'test_helper' | ||
|
||
module ActiveModel | ||
class Serializer | ||
module Adapter | ||
class JsonApi | ||
class IncludeParamTest < ActiveSupport::TestCase | ||
IncludeParamAuthor = Class.new(::Model) | ||
|
||
class CustomCommentLoader | ||
def all | ||
[{ foo: 'bar' }] | ||
end | ||
end | ||
|
||
class TagSerializer < ActiveModel::Serializer | ||
attributes :id, :name | ||
end | ||
|
||
class IncludeParamAuthorSerializer < ActiveModel::Serializer | ||
class_attribute :comment_loader | ||
|
||
associations_via_include_param(true) | ||
|
||
has_many :tags, serializer: TagSerializer do | ||
link :self, '//example.com/link_author/relationships/tags' | ||
end | ||
|
||
has_many :unlinked_tags, serializer: TagSerializer | ||
|
||
has_many :posts, serializer: PostWithTagsSerializer | ||
has_many :locations | ||
has_many :comments do | ||
load_data { IncludeParamAuthorSerializer.comment_loader.all } | ||
end | ||
end | ||
|
||
def setup | ||
IncludeParamAuthorSerializer.comment_loader = Class.new(CustomCommentLoader).new | ||
@tag = Tag.new(id: 1337, name: 'mytag') | ||
@author = IncludeParamAuthor.new( | ||
id: 1337, | ||
tags: [@tag] | ||
) | ||
end | ||
|
||
def test_relationship_not_loaded_when_not_included | ||
expected = { | ||
links: { | ||
self: '//example.com/link_author/relationships/tags' | ||
} | ||
} | ||
|
||
@author.define_singleton_method(:read_attribute_for_serialization) do |attr| | ||
fail 'should not be called' if attr == :tags | ||
super(attr) | ||
end | ||
|
||
assert_relationship(:tags, expected) | ||
end | ||
|
||
def test_relationship_included | ||
expected = { | ||
data: [ | ||
{ | ||
id: '1337', | ||
type: 'tags' | ||
} | ||
], | ||
links: { | ||
self: '//example.com/link_author/relationships/tags' | ||
} | ||
} | ||
|
||
assert_relationship(:tags, expected, include: :tags) | ||
end | ||
|
||
def test_nested_relationship | ||
expected = { | ||
data: [ | ||
{ | ||
id: '1337', | ||
type: 'tags' | ||
} | ||
], | ||
links: { | ||
self: '//example.com/link_author/relationships/tags' | ||
} | ||
} | ||
|
||
expected_no_data = { | ||
links: { | ||
self: '//example.com/link_author/relationships/tags' | ||
} | ||
} | ||
|
||
assert_relationship(:tags, expected, include: [:tags, { posts: :tags }]) | ||
|
||
@author.define_singleton_method(:read_attribute_for_serialization) do |attr| | ||
fail 'should not be called' if attr == :tags | ||
super(attr) | ||
end | ||
|
||
assert_relationship(:tags, expected_no_data, include: { posts: :tags }) | ||
end | ||
|
||
def test_include_params_with_no_block | ||
@author.define_singleton_method(:read_attribute_for_serialization) do |attr| | ||
fail 'should not be called' if attr == :locations | ||
super(attr) | ||
end | ||
|
||
expected = {} | ||
|
||
assert_relationship(:locations, expected) | ||
end | ||
|
||
def test_block_relationship | ||
expected = { | ||
data: [ | ||
{ 'foo' => 'bar' } | ||
] | ||
} | ||
|
||
assert_relationship(:comments, expected, include: [:comments]) | ||
end | ||
|
||
def test_block_relationship_not_included | ||
expected = {} | ||
|
||
IncludeParamAuthorSerializer.comment_loader.define_singleton_method(:all) do | ||
fail 'should not be called' | ||
end | ||
|
||
assert_relationship(:comments, expected) | ||
end | ||
|
||
def test_node_not_included_when_no_link | ||
expected = nil | ||
assert_relationship(:unlinked_tags, expected) | ||
end | ||
|
||
private | ||
|
||
def assert_relationship(relationship_name, expected, opts = {}) | ||
opts = { adapter: :json_api }.merge(opts) | ||
hash = serializable(@author, opts).serializable_hash | ||
assert_equal(expected, hash[:data][:relationships][relationship_name]) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters