jsonapi-rb / jsonapi-rails

Rails gem for fast jsonapi-compliant APIs.
http://jsonapi-rb.org
MIT License
321 stars 63 forks source link

Is there any way to pass arguments to a relationship resource? #110

Open connor-odoherty opened 5 years ago

connor-odoherty commented 5 years ago

Let's say we have a model Professor.rb that is serialized by the SerializableProfessor. The professor has the attributes:

In certain cases, depending on the class (or even higher up the chain, depending on the Student who has_many Lectures, which has_one Professor), I want to be able to return the phone_number as itself, or as nil.

In this contrived example, the Professor model does not know anything about Lectures. A Lecture has_one Professor, but the Professor DOES NOT have_many Lectures.

Is there any way to tell SerializableProfessor whether or not to show the phone_number from SerializableLecture?

Ideally, SerializableLecture would look like so:

class SerializableLecture < ApplicationSerializer
  ...
  ...
  has_one :professor do
    data do
      @object.professor
    end
    arguments do
      { show_phone_number: false }
    end
  end
end

and SerializableProfessor could do:

class SerializableProfessor < ApplicationSerializer
    extend JSONAPI::Serializable::Resource::ConditionalFields

   attribute :phone_number, unless -> { @arguments[:phone_number] }
end

OR

Even better, we could conditionally decide which Serializer to use. Maybe we have a base SerializableProfessor, and we have SerializableProfessorWithContactInfo and SerializableProfessorWithoutContactInfo. Then, we do something like:

class SerializableLecture < ApplicationSerializer
  ...
  ...
  has_one :professor do
    data do
      @object.professor
    end
    serializer_to_use do
      @object.is_a_lecture_with_contact_info? ? SerializableProfessorWithContactInfo : SerializableProfessorWithoutContactInfo
    end
  end
end

While I could use expose define a mapping of Professor UUIDs to whether or not I should be returning contact info, this could become performance costly at a large scale, non-contrived scenario.

Beyond what I have proposed, are there any other ways to achieve that desired behavior?

Additionally, if I can't do anything from the relationship perspective, is there any way to access the serialization capabilities of a single Serializable resource class? That way, instead of defining a has_one, I could do:

class SerializableLecture < ApplicationSerializer
  ...
  ...
  attribute :professor do
    serialized_professor = SerializableProfessor.new(@object.professor)
    serialized_professor[:phone_number] = @object.is_a_lecture_with_contact_info? ? @object.professor.phone_number : nil
    serialized_professor
  end
end

I understand that the issue here is: 'What happens if Professor A when referenced from Lecture A is supposed to show phone_number, but when referenced from Lecture B, they are not supposed to return phone_number?'. Maybe we can keep track of all arguments passed on a resource to resource level, and then allow the resource itself to determine what to do with those collected arguments?

allencch commented 1 year ago

I have similar situation. But I solve it by create another serializable class with inheritance.