fast_jsonapi icon indicating copy to clipboard operation
fast_jsonapi copied to clipboard

Bug: has_many with `id_method_name` returns only one object (instead of an array) and with the wrong ID

Open feliperaul opened this issue 7 years ago • 6 comments

This is very similar to #237, I think they might be related, so I'm going to use the same example.

Picture simple relationship like this:

class ASerializer
  include FastJsonapi::ObjectSerializer

  has_many :bs, id_method_name: :custom_id
end

class BSerializer
  include FastJsonapi::ObjectSerializer

  set_id :custom_id
end

As you can see, we have explicitly told it to use :custom_id as the foreign key for the has_many association. But the problem is when we run ASerializer.new(instance_of_a, {include: [:bs]}).serializable_hash, the generated hash is like this:

{
  data: [
    { id: AID,
      type: :a,
      attributes: {},
      relationships: {
       bs: {
        data: {id: "AID", :type=>:B}
      }
  ],
  included: [
    { id: B1CUSTOMID, ... },
    { id: B2CUSTOMID, ... },
  ]
}

As you can see, there's clearly a bug, because on relationships.bs, its returning an object (instead of an expected array, because it's a has_many) with just one element (even tough the example object has 2 associated Bs!), and the ID returned inside that object is actually A's own ID, which does NOT match any of the included IDs (because there you will find, as expected, B's ids).

If I simply remove the id_method_name from that has_many association, leaving it like this:

class ASerializer
  include FastJsonapi::ObjectSerializer

  has_many :bs
end

And run the exact same command, the generated output is like this:

{
  data: [
    { id: AID,
      type: :a,
      attributes: {},
      relationships: {
       bs: {
        data: [
         {id: "B1ID", :type=>:B}
         {id: "B2ID", :type=>:B}
       ]
      }
  ],
  included: [
    { id: B1CUSTOMID, ... },
    { id: B2CUSTOMID, ... },
  ]
}

As you see, then the generated Hash contains an array in relationships.bs, but it's also unusable because the IDs there won't match the IDs in the included key because there we use :custom_id.

Unfortunately I have zero experience in coding tests, so I can't help with writing one.

feliperaul avatar Jun 11 '18 02:06 feliperaul

I just confirmed it also happens with belongs_to: as well.

If it helps debugging (just a hint), my id_method_name is :id_hash, which is coincidentially the same name as some methods used by the gem.

feliperaul avatar Jun 11 '18 04:06 feliperaul

@shishirmk Did you manage to reproduce the bug?

feliperaul avatar Aug 15 '18 13:08 feliperaul

@feliperaul @shishirmk I ran into the same type of issue for belongs_to. This is what i have done:

class ASerializer
  include FastJsonapi::ObjectSerializer

  set_id :uuid
  belongs_to :b, id_method_name: :uuid
end

class BSerializer
  include FastJsonapi::ObjectSerializer

  set_id :uuid
end

I ran ASerializer.new(instance_of_a).serializable_hash and got following output:

{
  data: { 
      id: uuid_for_instance_of_a,
      type: :a,
      attributes: {},
      relationships: {
          b: {
            data: {
                id: uuid_for_instance_of_a,
                type: :b
             }
          }
     }
}

Expected Output:

{
  data: { 
      id: uuid_for_instance_of_a,
      type: :a,
      attributes: {},
      relationships: {
          b: {
            data: {
                id: uuid_of_b_to_which_a_belongs,
                type: :b
             }
          }
     }
}

rishabhsairawat avatar Sep 28 '18 07:09 rishabhsairawat

@henvo I understood your point. But: First, I think there should not be a need of using id_method_name to set id for the b for the relationships. It should use id from b's serializer. (Just Like AMS) Second, if id_method_name has to be used then it should be called upon relationship object. It does not make sense to set id of b's object by calling a method for a's object.

@shishirmk Please consider this. As current behavior does not make sense.

rishabhsairawat avatar Sep 28 '18 09:09 rishabhsairawat

@rishabhsairawat I completely agree.

henvo avatar Sep 28 '18 10:09 henvo

Just stumbled upon this issue myself and found a solution in a referenced link. https://github.com/Netflix/fast_jsonapi/issues/338#issuecomment-451205662

For the case of ASerializer having many BSerializer, a working example should be:

class ASerializer
  ...
  has_many :bs, id_method_name: :uuid do |object|
    object.bs
  end
end

But this being said I agree with @rishabhsairawat. The current behavior is a surprise and its workaround is redundant.

jakephot avatar Jul 28 '19 10:07 jakephot