Skip to content

page with nonzero offset fails to include relationships if it contains exactly one resource #1312

@ghost

Description

This issue is a (choose one):

  • Problem/bug report.
  • Feature request.
  • Request for support. Note: Please try to avoid submitting issues for support requests. Use Gitter instead.

Checklist before submitting:

  • I've searched for an existing issue.
  • I've asked my question on Gitter and have not received a satisfactory answer.
  • I've included a complete bug report template. This step helps us and allows us to see the bug without trying to reproduce the problem from your description. It helps you because you will frequently detect if it's a problem specific to your project.
  • The feature I'm asking for is compliant with the JSON:API spec.

Description

If a page with nonzero offset contains exactly one resource then the included relationships are nil (for has_one) or [] (for has_many).

# frozen_string_literal: true

begin
  require 'bundler/inline'
  require 'bundler'
rescue LoadError => e
  warn 'Bundler version 1.10 or later is required. Please update your Bundler'
  raise e
end

gemfile(true, ui: ENV['SILENT'] ? Bundler::UI::Silent.new : Bundler::UI::Shell.new) do
  source 'https://rubygems.org'

  gem 'rails', require: false
  gem 'sqlite3', platform: :mri

  if ENV['JSONAPI_RESOURCES_PATH']
    gem 'jsonapi-resources', path: ENV['JSONAPI_RESOURCES_PATH'], require: false
  else
    gem 'jsonapi-resources', git: 'https://github.com/cerebris/jsonapi-resources', require: false
  end
end

# prepare active_record database
require 'active_record'

class NullLogger < Logger
  def initialize(*_args); end

  def add(*_args, &_block); end
end

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = ENV['SILENT'] ? NullLogger.new : Logger.new(STDOUT)
ActiveRecord::Migration.verbose = !ENV['SILENT']

ActiveRecord::Schema.define do
  # Add your schema here
  create_table :users, force: true do |t|
    t.string :name, null: false
  end

  create_table :comments, force: true do |t|
    t.references :user, null: false, foreign_key: true, index: true
    t.string :title, null: false
  end
end

# create models
class User < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
end

# prepare rails app
require 'action_controller/railtie'
# require 'action_view/railtie'
require 'jsonapi-resources'

class ApplicationController < ActionController::Base
end

# prepare jsonapi resources and controllers
class UsersController < ApplicationController
  include JSONAPI::ActsAsResourceController
end

class CommentsController < ApplicationController
  include JSONAPI::ActsAsResourceController
end

class UserResource < JSONAPI::Resource
  attribute :name
  has_many :comments
end

class CommentResource < JSONAPI::Resource
  attribute :title
  has_one :user
end

class TestApp < Rails::Application
  config.root = File.dirname(__FILE__)
  config.logger = ENV['SILENT'] ? NullLogger.new : Logger.new(STDOUT)
  Rails.logger = config.logger

  secrets.secret_token = 'secret_token'
  secrets.secret_key_base = 'secret_key_base'

  config.eager_load = false
  config.hosts << 'example.org'
end

# initialize app
Rails.application.initialize!

JSONAPI.configure do |config|
  config.json_key_format = :underscored_key
  config.route_format = :underscored_key

  config.default_paginator = :paged
  config.default_page_size = 20
  config.maximum_page_size = 1000
end

# draw routes
Rails.application.routes.draw do
  jsonapi_resources :comments
end

# prepare tests
require 'minitest/autorun'
require 'rack/test'

# TODO: fixtures: how to correctly seed the test database once?
user = User.create! name: 'John Doe'
user.comments.create!(title: '1st comment')
user.comments.create!(title: '2nd comment')
user.comments.create!(title: '3rd comment')
user.comments.create!(title: '4th comment')
user.comments.create!(title: '5th comment')

# Replace this with the code necessary to make your test fail.
class BugTest < Minitest::Test
  include Rack::Test::Methods

  def json_api_headers
    { 'Accept' => JSONAPI::MEDIA_TYPE, 'CONTENT_TYPE' => JSONAPI::MEDIA_TYPE }
  end

  # test passes; demonstration only
  def test_user_included_on_first_page
    get '/comments?include=user&page[size]=2&page[number]=1', nil, json_api_headers
    json_response = JSON.parse(last_response.body)
    assert_equal 2, json_response['data'].length
    refute_nil json_response['data'].first['relationships']['user']['data']
  end

  # test passes; demonstration only
  def test_user_included_on_second_page
    get '/comments?include=user&page[size]=2&page[number]=2', nil, json_api_headers
    json_response = JSON.parse(last_response.body)
    assert_equal 2, json_response['data'].length
    refute_nil json_response['data'].first['relationships']['user']['data']
  end

  # this test fails because:
  #   * this page contains exactly one resource, and
  #   * the offset of this page is nonzero.
  # see https://github.com/cerebris/jsonapi-resources/blob/978f590f85fe9e65d5806f206b661fb35332c1bd/lib/jsonapi/active_relation_resource.rb#L398
  def test_user_included_on_last_page
    get '/comments?include=user&page[size]=2&page[number]=3', nil, json_api_headers
    json_response = JSON.parse(last_response.body)
    assert_equal 1, json_response['data'].length
    refute_nil json_response['data'].first['relationships']['user']['data']
  end

  private

  def app
    Rails.application
  end
end

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