Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions lib/jsonapi/processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,31 @@ def create_resource
resource = resource_klass.create(context)
result = resource.replace_fields(data)

options = {
context: context,
fields: fields,
filters: { resource_klass._primary_key => resource.id },
include_directives: include_directives
}

resource_set = find_resource_set(include_directives, options)

resource_set.populate!(serializer, context, options)
# PORO compatibility: if model doesn't respond to :all, use created resource directly
# This restores 0.9.x behavior for PORO models without requiring find_by_key override
if resource_klass._model_class.respond_to?(:all)
# ActiveRecord path: use find_resource_set for optimized queries with includes
options = {
context: context,
fields: fields,
filters: { resource_klass._primary_key => resource.id },
include_directives: include_directives
}

resource_set = find_resource_set(include_directives, options)
resource_set.populate!(serializer, context, options)
else
# PORO path: use created resource directly (0.9.x behavior)
# ResourceSet will use the resource without calling find
resource_set = JSONAPI::ResourceSet.new(resource, include_directives[:include_related], {
context: context,
fields: fields
})
resource_set.populate!(serializer, context, {
context: context,
fields: fields
})
end

JSONAPI::ResourceSetOperationResult.new((result == :completed ? :created : :accepted), resource_set, result_options)
end
Expand Down
57 changes: 57 additions & 0 deletions test/unit/resource/poro_resource_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ def initialize(id, name)
end

# Note: No .all method - this is a PORO, not ActiveRecord

# Required for jsonapi-resources compatibility
def valid?(_context = nil)
true
end

def save(_options = {})
true
end
end

# Resource for PORO model
Expand All @@ -32,6 +41,22 @@ def create(context)
end
end

# PORO Resource without find_by_key override (mimics CallbackTelephoneIncomingResource)
class MinimalPoroModelResource < JSONAPI::Resource
model_name 'PoroModel'
attributes :name

class << self
def create(context)
model = PoroModel.new(SecureRandom.uuid, "Minimal PORO")
new(model, context)
end

# Note: find_by_key is NOT overridden
# This should work with the 0.9.x-style create behavior
end
end

class PoroResourceTest < ActiveSupport::TestCase
def test_poro_model_does_not_respond_to_all
# Verify our test PORO doesn't have .all method
Expand Down Expand Up @@ -98,4 +123,36 @@ def test_active_record_resource_still_uses_original_find_fragments
assert_kind_of JSONAPI::ResourceFragment, fragment
end
end

def test_create_poro_without_find_by_key_override
# This tests the fix for PORO resources that don't override find_by_key
# Previously, create would call find_resource_set -> find_fragments -> _model_class.all
# which would fail for PORO models without .all method
# With the fix, create should return the created resource directly (0.9.x behavior)

params = {
data: {
type: 'minimal_poro_models',
attributes: {
name: 'Test PORO'
}
},
include_directives: JSONAPI::IncludeDirectives.new(MinimalPoroModelResource, []),
fields: {},
serializer: JSONAPI::ResourceSerializer.new(MinimalPoroModelResource)
}

processor = JSONAPI::Processor.new(MinimalPoroModelResource, :create_resource, params)

# This should not raise an error (no call to _model_class.all)
result = processor.create_resource

assert_kind_of JSONAPI::ResourceSetOperationResult, result
assert result.code == :created || result.code == 201, "Expected :created or 201, got #{result.code.inspect}"
assert_not_nil result.resource_set

# Verify the resource set contains the created resource
resources = result.resource_set.instance_variable_get(:@resource_klasses)
assert_equal 1, resources[MinimalPoroModelResource].length
end
end