Sitelet https://github.com/jsonapi-serializer/jsonapi-serializer/issues/245
Skip to content

Single Resource incorrectly identified as a collection #245

Description

@tilo

Expected Behavior

A single Object passed into a serializer should use hash_for_one_record

Actual Behavior

A single Object of type Stripe::PaymentIntent is using hash_for_collection instead

Steps to Reproduce the Problem

  1. get a single object of type Stripe::PaymentIntent, which responds to each, but not size or each_pair
  2. try to serialize it - it will break with fast_jsonapi version >= 1.7.2
  3. observe breakage, although resource responds to :id

Specifications

  • Version: >= 1.7.2
  • Ruby version: 3.2.2

Observations

> resource.class
=> Stripe::PaymentIntent
> resource.respond_to?(:id)
=> true
> resource.respond_to?(:size)
=> false
> resource.respond_to?(:each)
=> true
> resource.respond_to?(:each_pair)
=> false
> resource.id
=> "pi_3xxxxxxxxxxxxxxxxxxxxxxW"

Yes, it is odd that this Stripe object responds to :each for a single object, but shouldn't a response to :id be a stronger indicator that this is not a collection? 🤔

> x =  Stripe::PaymentIntent.new
=> #<Stripe::PaymentIntent:0x2feb8> JSON: {
}
> x.respond_to?(:each)
=> true

🤷

Root-Cause

This Change from April 11, 2020
from this PR
changed the behavior how #is_collection? determines if a resource is a collection.

Suggested Fix:

in is_collection? it might be more reliable to first check if the resource responds to :id. If so, it is a single resource, otherwise test for responds to :each, :each_pair, or :size

e.g.: instead of doing this:

    def is_collection?(resource, force_is_collection = nil)
      return force_is_collection unless force_is_collection.nil?

      resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
    end

Could we do this?

    def is_collection?(resource, force_is_collection = nil)
      return force_is_collection unless force_is_collection.nil?
      
      # shouldn't this be a good indicator of a single record?
      return false if resource.respond_to?(:id)

      # otherwise let's guess
      resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
    end

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions