Merge pull request #2216 from rails-api/serialize_resource_with_nil_id

Fix: Serialize resource type for unpersisted records (blank id)
This commit is contained in:
Benjamin Fleischer 2017-11-01 10:36:22 -05:00 committed by GitHub
commit 3c5e11bb0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 190 additions and 156 deletions

View File

@ -8,12 +8,13 @@ module ActiveModelSerializers
end end
def self.for_type_with_id(type, id, options) def self.for_type_with_id(type, id, options)
return nil if id.blank?
type = inflect_type(type) type = inflect_type(type)
{ type = type_for(:no_class_needed, type, options)
id: id.to_s, if id.blank?
type: type_for(:no_class_needed, type, options) { type: type }
} else
{ id: id.to_s, type: type }
end
end end
def self.raw_type_from_serializer_object(object) def self.raw_type_from_serializer_object(object)
@ -38,8 +39,11 @@ module ActiveModelSerializers
end end
def as_json def as_json
return nil if id.blank? if id.blank?
{ id: id, type: type } { type: type }
else
{ id: id.to_s, type: type }
end
end end
protected protected

View File

@ -1,110 +0,0 @@
require 'test_helper'
module ActiveModelSerializers
module Adapter
class JsonApi
class ResourceIdentifierTest < ActiveSupport::TestCase
class WithDefinedTypeSerializer < ActiveModel::Serializer
type 'with_defined_type'
end
class WithDefinedIdSerializer < ActiveModel::Serializer
def id
'special_id'
end
end
class FragmentedSerializer < ActiveModel::Serializer
cache only: :id
def id
'special_id'
end
end
setup do
@model = Author.new(id: 1, name: 'Steve K.')
ActionController::Base.cache_store.clear
end
def test_defined_type
test_type(WithDefinedTypeSerializer, 'with-defined-type')
end
def test_singular_type
test_type_inflection(AuthorSerializer, 'author', :singular)
end
def test_plural_type
test_type_inflection(AuthorSerializer, 'authors', :plural)
end
def test_type_with_namespace
Object.const_set(:Admin, Module.new)
model = Class.new(::Model)
Admin.const_set(:PowerUser, model)
serializer = Class.new(ActiveModel::Serializer)
Admin.const_set(:PowerUserSerializer, serializer)
with_namespace_separator '--' do
admin_user = Admin::PowerUser.new
serializer = Admin::PowerUserSerializer.new(admin_user)
expected = {
id: admin_user.id,
type: 'admin--power-users'
}
identifier = ResourceIdentifier.new(serializer, {})
actual = identifier.as_json
assert_equal(expected, actual)
end
end
def test_id_defined_on_object
test_id(AuthorSerializer, @model.id.to_s)
end
def test_id_defined_on_serializer
test_id(WithDefinedIdSerializer, 'special_id')
end
def test_id_defined_on_fragmented
test_id(FragmentedSerializer, 'special_id')
end
private
def test_type_inflection(serializer_class, expected_type, inflection)
original_inflection = ActiveModelSerializers.config.jsonapi_resource_type
ActiveModelSerializers.config.jsonapi_resource_type = inflection
test_type(serializer_class, expected_type)
ensure
ActiveModelSerializers.config.jsonapi_resource_type = original_inflection
end
def test_type(serializer_class, expected_type)
serializer = serializer_class.new(@model)
resource_identifier = ResourceIdentifier.new(serializer, nil)
expected = {
id: @model.id.to_s,
type: expected_type
}
assert_equal(expected, resource_identifier.as_json)
end
def test_id(serializer_class, id)
serializer = serializer_class.new(@model)
resource_identifier = ResourceIdentifier.new(serializer, nil)
inflection = ActiveModelSerializers.config.jsonapi_resource_type
type = @model.class.model_name.send(inflection)
expected = {
id: id,
type: type
}
assert_equal(expected, resource_identifier.as_json)
end
end
end
end
end

View File

@ -1,59 +1,191 @@
require 'test_helper' require 'test_helper'
module ActiveModel module ActiveModelSerializers
class Serializer module Adapter
module Adapter class JsonApi
class JsonApi class TypeTest < ActiveSupport::TestCase
class TypeTest < ActiveSupport::TestCase class StringTypeSerializer < ActiveModel::Serializer
class StringTypeSerializer < ActiveModel::Serializer attribute :name
attribute :name type 'profile'
type 'profile' end
end
class SymbolTypeSerializer < ActiveModel::Serializer class SymbolTypeSerializer < ActiveModel::Serializer
attribute :name attribute :name
type :profile type :profile
end end
setup do setup do
@author = Author.new(id: 1, name: 'Steve K.') @author = Author.new(id: 1, name: 'Steve K.')
end end
def test_config_plural def test_config_plural
with_jsonapi_resource_type :plural do with_jsonapi_inflection :plural do
assert_type(@author, 'authors') assert_type(@author, 'authors')
end
end end
end
def test_config_singular def test_config_singular
with_jsonapi_resource_type :singular do with_jsonapi_inflection :singular do
assert_type(@author, 'author') assert_type(@author, 'author')
end
end end
end
def test_explicit_string_type_value def test_explicit_string_type_value
assert_type(@author, 'profile', serializer: StringTypeSerializer) assert_type(@author, 'profile', serializer: StringTypeSerializer)
end
def test_explicit_symbol_type_value
assert_type(@author, 'profile', serializer: SymbolTypeSerializer)
end
private
def assert_type(resource, expected_type, opts = {})
opts = opts.reverse_merge(adapter: :json_api)
hash = serializable(resource, opts).serializable_hash
assert_equal(expected_type, hash.fetch(:data).fetch(:type))
end
end
class ResourceIdentifierTest < ActiveSupport::TestCase
class WithDefinedTypeSerializer < ActiveModel::Serializer
type 'with_defined_types'
end
class WithDefinedIdSerializer < ActiveModel::Serializer
def id
'special_id'
end end
end
def test_explicit_symbol_type_value class FragmentedSerializer < ActiveModel::Serializer
assert_type(@author, 'profile', serializer: SymbolTypeSerializer) cache only: :id
def id
'special_id'
end end
end
private setup do
@model = Author.new(id: 1, name: 'Steve K.')
ActionController::Base.cache_store.clear
end
def assert_type(resource, expected_type, opts = {}) def test_defined_type
opts = opts.reverse_merge(adapter: :json_api) actual = with_jsonapi_inflection :plural do
hash = serializable(resource, opts).serializable_hash actual_resource_identifier_object(WithDefinedTypeSerializer, @model)
assert_equal(expected_type, hash.fetch(:data).fetch(:type))
end end
expected = { id: expected_model_id(@model), type: 'with-defined-types' }
assert_equal actual, expected
end
def with_jsonapi_resource_type(inflection) def test_defined_type_not_inflected
old_inflection = ActiveModelSerializers.config.jsonapi_resource_type actual = with_jsonapi_inflection :singular do
ActiveModelSerializers.config.jsonapi_resource_type = inflection actual_resource_identifier_object(WithDefinedTypeSerializer, @model)
yield
ensure
ActiveModelSerializers.config.jsonapi_resource_type = old_inflection
end end
expected = { id: expected_model_id(@model), type: 'with-defined-types' }
assert_equal actual, expected
end
def test_singular_type
actual = with_jsonapi_inflection :singular do
actual_resource_identifier_object(AuthorSerializer, @model)
end
expected = { id: expected_model_id(@model), type: 'author' }
assert_equal actual, expected
end
def test_plural_type
actual = with_jsonapi_inflection :plural do
actual_resource_identifier_object(AuthorSerializer, @model)
end
expected = { id: expected_model_id(@model), type: 'authors' }
assert_equal actual, expected
end
def test_type_with_namespace
Object.const_set(:Admin, Module.new)
model = Class.new(::Model)
Admin.const_set(:PowerUser, model)
serializer = Class.new(ActiveModel::Serializer)
Admin.const_set(:PowerUserSerializer, serializer)
with_namespace_separator '--' do
admin_user = Admin::PowerUser.new
serializer = Admin::PowerUserSerializer.new(admin_user)
expected = {
id: admin_user.id,
type: 'admin--power-users'
}
identifier = ResourceIdentifier.new(serializer, {})
actual = identifier.as_json
assert_equal(expected, actual)
end
end
def test_id_defined_on_object
actual = actual_resource_identifier_object(AuthorSerializer, @model)
expected = { id: @model.id.to_s, type: expected_model_type(@model) }
assert_equal actual, expected
end
def test_blank_id
model = Author.new(id: nil, name: 'Steve K.')
actual = actual_resource_identifier_object(AuthorSerializer, model)
expected = { type: expected_model_type(model) }
assert_equal actual, expected
end
def test_for_type_with_id
id = 1
actual = ResourceIdentifier.for_type_with_id('admin_user', id, {})
expected = { id: '1', type: 'admin-users' }
assert_equal actual, expected
end
def test_for_type_with_id_given_blank_id
id = ''
actual = ResourceIdentifier.for_type_with_id('admin_user', id, {})
expected = { type: 'admin-users' }
assert_equal actual, expected
end
def test_for_type_with_id_inflected
id = 2
actual = with_jsonapi_inflection :singular do
ResourceIdentifier.for_type_with_id('admin_users', id, {})
end
expected = { id: '2', type: 'admin-user' }
assert_equal actual, expected
end
def test_id_defined_on_serializer
actual = actual_resource_identifier_object(WithDefinedIdSerializer, @model)
expected = { id: 'special_id', type: expected_model_type(@model) }
assert_equal actual, expected
end
def test_id_defined_on_fragmented
actual = actual_resource_identifier_object(FragmentedSerializer, @model)
expected = { id: 'special_id', type: expected_model_type(@model) }
assert_equal actual, expected
end
private
def actual_resource_identifier_object(serializer_class, model)
serializer = serializer_class.new(model)
resource_identifier = ResourceIdentifier.new(serializer, nil)
resource_identifier.as_json
end
def expected_model_type(model, inflection = ActiveModelSerializers.config.jsonapi_resource_type)
with_jsonapi_inflection inflection do
model.class.model_name.send(inflection)
end
end
def expected_model_id(model)
model.id.to_s
end end
end end
end end

View File

@ -47,6 +47,14 @@ module SerializationTesting
ActiveModelSerializers.config.replace(old_config) ActiveModelSerializers.config.replace(old_config)
end end
def with_jsonapi_inflection(inflection)
original_inflection = ActiveModelSerializers.config.jsonapi_resource_type
ActiveModelSerializers.config.jsonapi_resource_type = inflection
yield
ensure
ActiveModelSerializers.config.jsonapi_resource_type = original_inflection
end
def with_serializer_lookup_disabled def with_serializer_lookup_disabled
original_serializer_lookup = ActiveModelSerializers.config.serializer_lookup_enabled original_serializer_lookup = ActiveModelSerializers.config.serializer_lookup_enabled
ActiveModelSerializers.config.serializer_lookup_enabled = false ActiveModelSerializers.config.serializer_lookup_enabled = false