mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
Merge pull request #1239 from beauby/fix-duplicate-jsonapi
Fix duplicate resources inside included in compound document.
This commit is contained in:
commit
49eb531539
@ -21,6 +21,7 @@ Features:
|
|||||||
- [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4)
|
- [#1050](https://github.com/rails-api/active_model_serializers/pull/1050) Add support for toplevel jsonapi member (@beauby, @bf4)
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
|
- [#1239](https://github.com/rails-api/active_model_serializers/pull/1239) Fix duplicates in JSON API compound documents (@beauby)
|
||||||
- [#1214](https://github.com/rails-api/active_model_serializers/pull/1214) retrieve the key from the reflection options when building associations (@NullVoxPopuli, @hut8)
|
- [#1214](https://github.com/rails-api/active_model_serializers/pull/1214) retrieve the key from the reflection options when building associations (@NullVoxPopuli, @hut8)
|
||||||
|
|
||||||
Misc:
|
Misc:
|
||||||
|
|||||||
@ -81,16 +81,18 @@ module ActiveModel
|
|||||||
|
|
||||||
def serializable_hash_for_collection(options)
|
def serializable_hash_for_collection(options)
|
||||||
hash = { data: [] }
|
hash = { data: [] }
|
||||||
|
included = []
|
||||||
serializer.each do |s|
|
serializer.each do |s|
|
||||||
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options)
|
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options)
|
||||||
hash[:data] << result[:data]
|
hash[:data] << result[:data]
|
||||||
|
next unless result[:included]
|
||||||
|
|
||||||
if result[:included]
|
included |= result[:included]
|
||||||
hash[:included] ||= []
|
|
||||||
hash[:included] |= result[:included]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
included.delete_if { |resource| hash[:data].include?(resource) }
|
||||||
|
hash[:included] = included if included.any?
|
||||||
|
|
||||||
if serializer.paginated?
|
if serializer.paginated?
|
||||||
hash[:links] ||= {}
|
hash[:links] ||= {}
|
||||||
hash[:links].update(links_for(serializer, options))
|
hash[:links].update(links_for(serializer, options))
|
||||||
@ -102,9 +104,10 @@ module ActiveModel
|
|||||||
def serializable_hash_for_single_resource
|
def serializable_hash_for_single_resource
|
||||||
primary_data = primary_data_for(serializer)
|
primary_data = primary_data_for(serializer)
|
||||||
relationships = relationships_for(serializer)
|
relationships = relationships_for(serializer)
|
||||||
included = included_resources(@include_tree)
|
primary_data[:relationships] = relationships if relationships.any?
|
||||||
hash = { data: primary_data }
|
hash = { data: primary_data }
|
||||||
hash[:data][:relationships] = relationships if relationships.any?
|
|
||||||
|
included = included_resources(@include_tree, [primary_data])
|
||||||
hash[:included] = included if included.any?
|
hash[:included] = included if included.any?
|
||||||
|
|
||||||
hash
|
hash
|
||||||
@ -171,31 +174,31 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def included_resources(include_tree)
|
def included_resources(include_tree, primary_data)
|
||||||
included = []
|
included = []
|
||||||
|
|
||||||
serializer.associations(include_tree).each do |association|
|
serializer.associations(include_tree).each do |association|
|
||||||
add_included_resources_for(association.serializer, include_tree[association.key], included)
|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
|
||||||
end
|
end
|
||||||
|
|
||||||
included
|
included
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_included_resources_for(serializer, include_tree, included)
|
def add_included_resources_for(serializer, include_tree, primary_data, included)
|
||||||
if serializer.respond_to?(:each)
|
if serializer.respond_to?(:each)
|
||||||
serializer.each { |s| add_included_resources_for(s, include_tree, included) }
|
serializer.each { |s| add_included_resources_for(s, include_tree, primary_data, included) }
|
||||||
else
|
else
|
||||||
return unless serializer && serializer.object
|
return unless serializer && serializer.object
|
||||||
|
|
||||||
primary_data = primary_data_for(serializer)
|
resource_object = primary_data_for(serializer)
|
||||||
relationships = relationships_for(serializer)
|
relationships = relationships_for(serializer)
|
||||||
primary_data[:relationships] = relationships if relationships.any?
|
resource_object[:relationships] = relationships if relationships.any?
|
||||||
|
|
||||||
return if included.include?(primary_data)
|
return if included.include?(resource_object) || primary_data.include?(resource_object)
|
||||||
included.push(primary_data)
|
included.push(resource_object)
|
||||||
|
|
||||||
serializer.associations(include_tree).each do |association|
|
serializer.associations(include_tree).each do |association|
|
||||||
add_included_resources_for(association.serializer, include_tree[association.key], included)
|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
|
NestedPost = Class.new(Model)
|
||||||
|
class NestedPostSerializer < ActiveModel::Serializer
|
||||||
|
has_many :nested_posts
|
||||||
|
end
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Serializer
|
class Serializer
|
||||||
module Adapter
|
module Adapter
|
||||||
class JsonApi
|
class JsonApi
|
||||||
class LinkedTest < Minitest::Test
|
class LinkedTest < Minitest::Test
|
||||||
def setup
|
def setup
|
||||||
ActionController::Base.cache_store.clear
|
|
||||||
@author1 = Author.new(id: 1, name: 'Steve K.')
|
@author1 = Author.new(id: 1, name: 'Steve K.')
|
||||||
@author2 = Author.new(id: 2, name: 'Tenderlove')
|
@author2 = Author.new(id: 2, name: 'Tenderlove')
|
||||||
@bio1 = Bio.new(id: 1, content: 'AMS Contributor')
|
@bio1 = Bio.new(id: 1, content: 'AMS Contributor')
|
||||||
@ -277,6 +282,112 @@ module ActiveModel
|
|||||||
assert_equal expected, adapter.serializable_hash
|
assert_equal expected, adapter.serializable_hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class NoDuplicatesTest < Minitest::Test
|
||||||
|
Post = Class.new(::Model)
|
||||||
|
Author = Class.new(::Model)
|
||||||
|
|
||||||
|
class PostSerializer < ActiveModel::Serializer
|
||||||
|
type 'posts'
|
||||||
|
belongs_to :author
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthorSerializer < ActiveModel::Serializer
|
||||||
|
type 'authors'
|
||||||
|
has_many :posts
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@author = Author.new(id: 1, posts: [], roles: [], bio: nil)
|
||||||
|
@post1 = Post.new(id: 1, author: @author)
|
||||||
|
@post2 = Post.new(id: 2, author: @author)
|
||||||
|
@author.posts << @post1
|
||||||
|
@author.posts << @post2
|
||||||
|
|
||||||
|
@nestedpost1 = ::NestedPost.new(id: 1, nested_posts: [])
|
||||||
|
@nestedpost2 = ::NestedPost.new(id: 2, nested_posts: [])
|
||||||
|
@nestedpost1.nested_posts << @nestedpost1
|
||||||
|
@nestedpost1.nested_posts << @nestedpost2
|
||||||
|
@nestedpost2.nested_posts << @nestedpost1
|
||||||
|
@nestedpost2.nested_posts << @nestedpost2
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_no_duplicates
|
||||||
|
hash = ActiveModel::SerializableResource.new(@post1, adapter: :json_api,
|
||||||
|
include: '*.*')
|
||||||
|
.serializable_hash
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
type: 'authors', id: '1',
|
||||||
|
relationships: {
|
||||||
|
posts: {
|
||||||
|
data: [
|
||||||
|
{ type: 'posts', id: '1' },
|
||||||
|
{ type: 'posts', id: '2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'posts', id: '2',
|
||||||
|
relationships: {
|
||||||
|
author: {
|
||||||
|
data: { type: 'authors', id: '1' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
assert_equal(expected, hash[:included])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_no_duplicates_collection
|
||||||
|
hash = ActiveModel::SerializableResource.new(
|
||||||
|
[@post1, @post2], adapter: :json_api,
|
||||||
|
include: '*.*')
|
||||||
|
.serializable_hash
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
type: 'authors', id: '1',
|
||||||
|
relationships: {
|
||||||
|
posts: {
|
||||||
|
data: [
|
||||||
|
{ type: 'posts', id: '1' },
|
||||||
|
{ type: 'posts', id: '2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
assert_equal(expected, hash[:included])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_no_duplicates_global
|
||||||
|
hash = ActiveModel::SerializableResource.new(
|
||||||
|
@nestedpost1,
|
||||||
|
adapter: :json_api,
|
||||||
|
include: '*').serializable_hash
|
||||||
|
expected = [
|
||||||
|
type: 'nested_posts', id: '2',
|
||||||
|
relationships: {
|
||||||
|
nested_posts: {
|
||||||
|
data: [
|
||||||
|
{ type: 'nested_posts', id: '1' },
|
||||||
|
{ type: 'nested_posts', id: '2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
assert_equal(expected, hash[:included])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_no_duplicates_collection_global
|
||||||
|
hash = ActiveModel::SerializableResource.new(
|
||||||
|
[@nestedpost1, @nestedpost2],
|
||||||
|
adapter: :json_api,
|
||||||
|
include: '*').serializable_hash
|
||||||
|
assert_nil(hash[:included])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user