diff --git a/lib/jsonapi/processor.rb b/lib/jsonapi/processor.rb index 814642a4..a3a9ceb6 100644 --- a/lib/jsonapi/processor.rb +++ b/lib/jsonapi/processor.rb @@ -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 diff --git a/test/unit/resource/poro_resource_test.rb b/test/unit/resource/poro_resource_test.rb index 619a99e0..724524b8 100644 --- a/test/unit/resource/poro_resource_test.rb +++ b/test/unit/resource/poro_resource_test.rb @@ -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 @@ -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 @@ -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