Netflix / fast_jsonapi

No Longer Maintained - A lightning fast JSON:API serializer for Ruby Objects.
Apache License 2.0
5.07k stars 425 forks source link

Relationships with serializer option don't work #443

Closed marceloperini closed 4 years ago

marceloperini commented 5 years ago

Summary

When define a serializer with has_many, has_one or belongs_to relationship with serializer option, it not being used the correct serializer class for relationship objects.

Steps to reproduce

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails"
  gem "sqlite3"
  gem "fast_jsonapi", github: "Netflix/fast_jsonapi"
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
    t.text :message
  end

  create_table :comments, force: true do |t|
    t.integer :post_id

    t.text :message
  end
end

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

class CommentSerializer
  include FastJsonapi::ObjectSerializer

  attributes :message
end

class PostSerializer
  include FastJsonapi::ObjectSerializer

  has_many :comments, serializer: ::CommentSerializer

  attributes :message
end

class SerializerTest < Minitest::Test
  def setup
    @post = Post.create(message: 'test message')
    @comment = Comment.create(message: 'test message',
                              post_id: @post.id)
  end

  def test_post_serializer
    post_serializer = PostSerializer.new(@post).serializable_hash

    assert_equal post_serializer, {
      data: {
        id: @post.id.to_s,
        type: :post,
        attributes: {
          message: @post.message
        },
        relationships: {
          comments: {
            data: [
              {
                id: @comment.id.to_s,
                type: :comment,
                attributes: {
                  message: @comment.message
                }
              }
            ]
          }
        }
      }
    }
  end

  def test_comment_serializer
    comment_serializer = CommentSerializer.new(@comment).serializable_hash

    assert_equal comment_serializer, {
      data: {
        id: @comment.id.to_s,
        type: :comment,
        attributes: {
          message: @comment.message
        }
      }
    }
  end
end

Expected behavior

When defining a serializer class for relationships it is expected to use the correct class.

Actual behavior

F

Failure:
SerializerTest#test_post_serializer [fast_jsonapi_test.rb:68]:
--- expected
+++ actual
@@ -1 +1 @@
-{:data=>{:id=>"1", :type=>:post, :attributes=>{:message=>"test message"}, :relationships=>{:comments=>{:data=>[{:id=>"1", :type=>:comment}]}}}}
+{:data=>{:id=>"1", :type=>:post, :attributes=>{:message=>"test message"}, :relationships=>{:comments=>{:data=>[{:id=>"1", :type=>:comment, :attributes=>{:message=>"test message"}}]}}}}

rails test fast_jsonapi_test.rb:65

D, [2019-09-14T18:40:41.825060 #46314] DEBUG -- :    (0.4ms)  begin transaction
D, [2019-09-14T18:40:41.826513 #46314] DEBUG -- :   Post Create (0.8ms)  INSERT INTO "posts" ("message") VALUES (?)  [["message", "test message"]]
D, [2019-09-14T18:40:41.826874 #46314] DEBUG -- :    (0.1ms)  commit transaction
D, [2019-09-14T18:40:41.827544 #46314] DEBUG -- :    (0.1ms)  begin transaction
D, [2019-09-14T18:40:41.827886 #46314] DEBUG -- :   Comment Create (0.2ms)  INSERT INTO "comments" ("post_id", "message") VALUES (?, ?)  [["post_id", 2], ["message", "test message"]]
D, [2019-09-14T18:40:41.828076 #46314] DEBUG -- :    (0.0ms)  commit transaction
.

Finished in 0.028896s, 69.2128 runs/s, 69.2128 assertions/s.
2 runs, 2 assertions, 1 failures, 0 errors, 0 skips

System configuration

Ruby version: 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux] FastJson version: master

SeanJManning commented 5 years ago

@marceloperini

See the answer in this StackOverFlow post:

https://stackoverflow.com/questions/55782066/how-to-get-the-fast-jsonapi-to-return-the-attributes-of-the-relationships

joeytheman commented 4 years ago

The stackoverflow post shows that an included attribute should be rendered. Looking at the test result, the included attribute is not rendered.

I am experiencing the same issue on Rails 6.

joeytheman commented 4 years ago

In order for the custom serializer to take effect, you have to specify the include option when initializing the serializer. The documentation does not make that clear.

IE

This

 post_serializer = PostSerializer.new(@post).serializable_hash

needs to change to

post_serializer = PostSerializer.new(@post, include: [:comments]).serializable_hash
benwalsh commented 4 years ago

This usage is tricky -- you have to pass custom options every time, instead of the attributes being consistent and defined in the Serializer itself. I was expecting to have something like:

class ProductSerializer
  include FastJsonapi::ObjectSerializer

  included :components, serializer: ProductComponentSerializer

or even

  has_many :components, include: { serializer: ProductComponentSerializer }
marceloperini commented 4 years ago

Thanks for the clarifications, it help me a lot. But I think this has to be more clear on the documentation