Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: format model polymorphic type from resource object type #1435

Merged
merged 8 commits into from
Jan 22, 2024

Conversation

bf4
Copy link
Collaborator

@bf4 bf4 commented Jan 22, 2024

In our app which is currently on the latest 0.9 we have a test
failing on the 0-11-dev branch (which was also failing on 0-10)

which posts the JSON body

{
  "data": {
    "type": "broker-memberships",
    "relationships": {
      "organization": {
        "data": {
          "type": "brokers",
          "id": "1"
        }
      },
      "user": {
        "data": {
          "type": "users",
          "id": "2"
        }
      }
    },
    "attributes": {
      "external-employee-id": "123"
    }
  },
}

to v1/broker-memberships

BrokerMembership is an STI ActiveRecord model
below Membership like below.

class Membership < ApplicationRecord
  belongs_to :organization, optional: false, polymorphic: true
  belongs_to :user, optional: false
end
class BrokerMembership < Membership
  validates :organization_type, inclusion: {in: ["Broker"], message: "must be Broker"}
end
class User < ApplicationRecord
end
class Broker < ApplicationRecord
end

our BrokerMembership resource looks like

module V1
  class MembershipResource < BaseResource

    has_one :organization,
      polymorphic: true,
      always_include_linkage_data: true,
      eager_load_on_include: false,
      create_implicit_polymorphic_type_relationships: false

    has_one :user,
      always_include_linkage_data: true
  end
end

In _replace_fields you can see the parsed field_data is

{
  :attributes=>{
    :external_employee_id=>"123"
  },
  :to_one=>{
    :organization=>{:id=>1, :type=>:brokers},
    :user=>2
  },
  :to_many=>{
  }
}

And in the call to _replace_polymorphic_to_one_link (as defined in
JSONAPI::ResourceCommon)

def _replace_polymorphic_to_one_link(relationship_type, key_value, key_type, _options)
  # relationship_type #=> "organization"
  relationship = self.class._relationship(relationship_type.to_sym)

  # self.organization_id = { type:  :brokers, id: 1 }
  send("#{relationship.foreign_key}=", {type: key_type, id: key_value})
  @save_needed = true

  :completed
end

calls the method defined by JSONAPI::ResourceCommon.define_on_resource like

def define_foreign_key_setter(relationship)
  if relationship.polymorphic?
    define_on_resource "#{relationship.foreign_key}=" do |v|
      _model.method("#{relationship.foreign_key}=").call(v[:id])
      _model.public_send("#{relationship.polymorphic_type}=", v[:type])
    end

which practially speaking calls

_model.organization_id = 1
_model.organization_type = :brokers

This latter behavior causes a failure since the 'organization_type' should be 'Broker' and not ':brokers'.

I'm not sure if this should be addressed in the definition of define_on_resource (by making it lookup the polymorphic type)
or in the JSONAPI::Request(Parser).parse_to_one_relationship (by removing the unformat and calling classify) or elsewhere.

All Submissions:

  • I've checked to ensure there aren't other open Pull Requests for the same update/change.
  • I've submitted a ticket for my issue if one did not already exist.
  • My submission passes all tests. (Please run the full test suite locally to cut down on noise from travis failures.)
  • I've used Github auto-closing keywords in the commit message or the description.
  • I've added/updated tests for this change.

New Feature Submissions:

  • I've submitted an issue that describes this feature, and received the go ahead from the maintainers.
  • My submission includes new tests.
  • My submission maintains compliance with JSON:API.

Bug fixes and Changes to Core Features:

  • I've included an explanation of what the changes do and why I'd like you to include them.
  • I've provided test(s) that fails without the change.

Test Plan:

Reviewer Checklist:

  • Maintains compliance with JSON:API
  • Adequate test coverage exists to prevent regressions

@bf4 bf4 force-pushed the polymorphic_request_parsing branch from a45b85d to 4d92877 Compare January 22, 2024 02:42
@@ -743,13 +747,13 @@ class Boat < Vehicle
end

class Document < ActiveRecord::Base
has_many :pictures, as: :imageable
has_many :pictures, as: :imageable # polymorphic
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for greppability

lib/jsonapi/resource_common.rb Outdated Show resolved Hide resolved
lib/jsonapi/resource_common.rb Outdated Show resolved Hide resolved
lib/jsonapi/resource_common.rb Outdated Show resolved Hide resolved
@bf4 bf4 changed the title test: failing request posting sti with polymorphic has one fix: format model polymorphic type from resource object type Jan 22, 2024
lib/jsonapi/resource_common.rb Outdated Show resolved Hide resolved
@@ -578,6 +578,10 @@ def resource_type_for(model)
end
end

def polymorphic_type_for(model_name)
model_name&.to_s&.classify
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@@ -1200,12 +1204,12 @@ def define_relationship_methods(relationship_name, relationship_klass, options)
def define_foreign_key_setter(relationship)
if relationship.polymorphic?
define_on_resource "#{relationship.foreign_key}=" do |v|
_model.method("#{relationship.foreign_key}=").call(v[:id])
_model.public_send("#{relationship.polymorphic_type}=", v[:type])
_model.public_send("#{relationship.foreign_key}=", v[:id])
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the instantiation of the method object here in favor of public send since we couldn't think of a reason the method object would be preferable

_model.method("#{relationship.foreign_key}=").call(v[:id])
_model.public_send("#{relationship.polymorphic_type}=", v[:type])
_model.public_send("#{relationship.foreign_key}=", v[:id])
_model.public_send("#{relationship.polymorphic_type}=", self.class.polymorphic_type_for(v[:type]))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔨 the fix

@bf4 bf4 merged commit 311b1fe into v0-11-dev Jan 22, 2024
39 checks passed
@bf4 bf4 deleted the polymorphic_request_parsing branch January 22, 2024 23:56
lgebhardt added a commit that referenced this pull request Apr 18, 2024
* test: failing request posting sti with polymorphic has one
* fix: polymorphic resource assignment
* Add polymorphic_type_for method
* Favor classify over singularize.camelize

---------

Co-authored-by: lgebhardt <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants