From 9f3e2849b56eb3ebc0c09870bf056e164e09821d Mon Sep 17 00:00:00 2001 From: Larry Gebhardt Date: Wed, 10 Mar 2021 11:43:13 -0500 Subject: [PATCH 1/2] Add STI relationship option --- lib/jsonapi/active_relation_resource.rb | 50 +++++++++++++++++++---- lib/jsonapi/basic_resource.rb | 4 +- lib/jsonapi/relationship.rb | 9 ++-- lib/jsonapi/request_parser.rb | 2 +- test/controllers/controller_test.rb | 18 ++++++++ test/fixtures/active_record.rb | 30 +++++++------- test/fixtures/people.yml | 1 + test/integration/requests/request_test.rb | 8 ++-- test/unit/serializer/serializer_test.rb | 12 ++++++ 9 files changed, 101 insertions(+), 33 deletions(-) diff --git a/lib/jsonapi/active_relation_resource.rb b/lib/jsonapi/active_relation_resource.rb index cb60faabd..89a66f84e 100644 --- a/lib/jsonapi/active_relation_resource.rb +++ b/lib/jsonapi/active_relation_resource.rb @@ -92,6 +92,9 @@ def find_to_populate_by_keys(keys, options = {}) def find_fragments(filters, options = {}) include_directives = options[:include_directives] ? options[:include_directives].include_directives : {} resource_klass = self + + fragments = {} + linkage_relationships = to_one_relationships_for_linkage(include_directives[:include_related]) sort_criteria = options.fetch(:sort_criteria) { [] } @@ -129,19 +132,33 @@ def find_fragments(filters, options = {}) if linkage_relationship.polymorphic? && linkage_relationship.belongs_to? linkage_relationship.resource_types.each do |resource_type| klass = resource_klass_for(resource_type) - linkage_fields << {relationship_name: name, resource_klass: klass} - linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias] primary_key = klass._primary_key + + linkage_fields << {relationship_name: name, + linkage_relationship: linkage_relationship, + resource_klass: klass, + field: "#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}", + alias: "#{linkage_table_alias}_#{primary_key}"} + pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}") end else klass = linkage_relationship.resource_klass - linkage_fields << {relationship_name: name, resource_klass: klass} - linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias] primary_key = klass._primary_key + + linkage_fields << {relationship_name: name, + linkage_relationship: linkage_relationship, + resource_klass: klass, + field: "#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}", + alias: "#{linkage_table_alias}_#{primary_key}"} + pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}") + + if linkage_relationship.sti? + pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, 'type')} AS #{linkage_table_alias}_type") + end end end @@ -158,7 +175,6 @@ def find_fragments(filters, options = {}) pluck_fields << Arel.sql(field) end - fragments = {} rows = records.pluck(*pluck_fields) rows.each do |row| rid = JSONAPI::ResourceIdentity.new(resource_klass, pluck_fields.length == 1 ? row : row[0]) @@ -175,7 +191,14 @@ def find_fragments(filters, options = {}) fragments[rid].initialize_related(linkage_field_details[:relationship_name]) related_id = row[attributes_offset] if related_id - related_rid = JSONAPI::ResourceIdentity.new(linkage_field_details[:resource_klass], related_id) + if linkage_field_details[:linkage_relationship].sti? + type = row[2] + related_rid = JSONAPI::ResourceIdentity.new(resource_klass_for(type), related_id) + attributes_offset+= 1 + else + related_rid = JSONAPI::ResourceIdentity.new(linkage_field_details[:resource_klass], related_id) + end + fragments[rid].add_related_identity(linkage_field_details[:relationship_name], related_rid) end attributes_offset+= 1 @@ -413,6 +436,10 @@ def find_related_monomorphic_fragments(source_rids, relationship, options, conne Arel.sql("#{concat_table_field(resource_table_alias, resource_klass._primary_key)} AS #{resource_table_alias}_#{resource_klass._primary_key}") ] + if relationship.sti? + pluck_fields << Arel.sql("#{concat_table_field(resource_table_alias, 'type')} AS #{resource_table_alias}_type") + end + cache_field = resource_klass.attribute_to_model_field(:_cache_field) if options[:cache] if cache_field pluck_fields << Arel.sql("#{concat_table_field(resource_table_alias, cache_field[:name])} AS #{resource_table_alias}_#{cache_field[:name]}") @@ -458,12 +485,17 @@ def find_related_monomorphic_fragments(source_rids, relationship, options, conne fragments = {} rows = records.distinct.pluck(*pluck_fields) rows.each do |row| - rid = JSONAPI::ResourceIdentity.new(resource_klass, row[1]) + if relationship.sti? + type = row[2] + rid = JSONAPI::ResourceIdentity.new(resource_klass_for(type), row[1]) + attributes_offset = 3 + else + rid = JSONAPI::ResourceIdentity.new(resource_klass, row[1]) + attributes_offset = 2 + end fragments[rid] ||= JSONAPI::ResourceFragment.new(rid) - attributes_offset = 2 - if cache_field fragments[rid].cache = cast_to_attribute_type(row[attributes_offset], cache_field[:type]) attributes_offset+= 1 diff --git a/lib/jsonapi/basic_resource.rb b/lib/jsonapi/basic_resource.rb index ea8b19ea7..a14b66a61 100644 --- a/lib/jsonapi/basic_resource.rb +++ b/lib/jsonapi/basic_resource.rb @@ -298,7 +298,7 @@ def _replace_to_many_links(relationship_type, relationship_key_values, options) _create_to_many_links(relationship_type, to_add, {}) @reload_needed = true - elsif relationship.polymorphic? + elsif relationship.polymorphic? || relationship.sti? relationship_key_values.each do |relationship_key_value| relationship_resource_klass = self.class.resource_klass_for(relationship_key_value[:type]) ids = relationship_key_value[:ids] @@ -1094,7 +1094,7 @@ def define_relationship_methods(relationship_name, relationship_klass, options) end def define_foreign_key_setter(relationship) - if relationship.polymorphic? + if relationship.polymorphic? || relationship.sti? define_on_resource "#{relationship.foreign_key}=" do |v| _model.method("#{relationship.foreign_key}=").call(v[:id]) _model.public_send("#{relationship.polymorphic_type}=", v[:type]) diff --git a/lib/jsonapi/relationship.rb b/lib/jsonapi/relationship.rb index 77e700b78..6c860691b 100644 --- a/lib/jsonapi/relationship.rb +++ b/lib/jsonapi/relationship.rb @@ -1,7 +1,7 @@ module JSONAPI class Relationship attr_reader :acts_as_set, :foreign_key, :options, :name, - :class_name, :polymorphic, :always_include_optional_linkage_data, + :class_name, :polymorphic, :sti, :always_include_optional_linkage_data, :parent_resource, :eager_load_on_include, :custom_methods, :inverse_relationship, :allow_include @@ -22,6 +22,7 @@ def initialize(name, options = {}) ActiveSupport::Deprecation.warn('Use polymorphic_types instead of polymorphic_relations') @polymorphic_types ||= options[:polymorphic_relations] end + @sti = options.fetch(:sti, false) @always_include_optional_linkage_data = options.fetch(:always_include_optional_linkage_data, false) == true @eager_load_on_include = options.fetch(:eager_load_on_include, false) == true @@ -39,6 +40,8 @@ def initialize(name, options = {}) end alias_method :polymorphic?, :polymorphic + alias_method :sti?, :sti + alias_method :parent_resource_klass, :parent_resource def primary_key @@ -63,12 +66,12 @@ def self.polymorphic_types(name) next unless Module === klass if ActiveRecord::Base > klass klass.reflect_on_all_associations(:has_many).select{|r| r.options[:as] }.each do |reflection| - (hash[reflection.options[:as]] ||= []) << klass.name.downcase + (hash[reflection.options[:as]] ||= []) << klass.name.underscore end end end end - @poly_hash[name.to_sym] + @poly_hash[name.to_sym] || [] end def resource_types diff --git a/lib/jsonapi/request_parser.rb b/lib/jsonapi/request_parser.rb index 4d72e911e..0af59da7a 100644 --- a/lib/jsonapi/request_parser.rb +++ b/lib/jsonapi/request_parser.rb @@ -567,7 +567,7 @@ def parse_to_many_relationship(resource_klass, link_value, relationship, &add_re if links_object.length == 0 add_result.call([]) else - if relationship.polymorphic? + if relationship.polymorphic? || relationship.sti? polymorphic_results = [] links_object.each_pair do |type, keys| diff --git a/test/controllers/controller_test.rb b/test/controllers/controller_test.rb index e815a0e24..66c499d76 100644 --- a/test/controllers/controller_test.rb +++ b/test/controllers/controller_test.rb @@ -2726,6 +2726,12 @@ def test_show_related_resource_no_namespace "self" => "http://test.host/people/1001/relationships/expense_entries", "related" => "http://test.host/people/1001/expense_entries" } + }, + "favorite-vehicle" => { + "links" => { + "self" => "http://test.host/people/1001/relationships/favorite_vehicle", + "related" => "http://test.host/people/1001/favorite_vehicle" + } } } } @@ -2747,6 +2753,18 @@ def test_show_related_resource_includes JSONAPI.configuration = original_config end + def test_show_include_sti + original_config = JSONAPI.configuration.dup + JSONAPI.configuration.json_key_format = :dasherized_key + JSONAPI.configuration.route_format = :underscored_key + get :show, params: {id: '1005', include: 'vehicles,favorite-vehicle'} + assert_response :success + assert_equal 'cars', json_response['included'][0]['type'] + assert_equal 'cars', json_response['data']['relationships']['favorite-vehicle']['data']['type'] + ensure + JSONAPI.configuration = original_config + end + def test_show_related_resource_nil assert_cacheable_get :show_related_resource, params: {post_id: '17', relationship: 'author', source:'posts'} assert_response :success diff --git a/test/fixtures/active_record.rb b/test/fixtures/active_record.rb index c1f3cc674..520a7b614 100644 --- a/test/fixtures/active_record.rb +++ b/test/fixtures/active_record.rb @@ -43,6 +43,7 @@ t.boolean :book_admin, default: false t.boolean :special, default: false t.timestamps null: false + t.integer :favorite_vehicle_id, index: true end create_table :author_details, force: true do |t| @@ -439,11 +440,11 @@ class Session < ActiveRecord::Base class Response < ActiveRecord::Base belongs_to :session - has_one :paragraph, :class_name => "ResponseText::Paragraph" + has_one :paragraph, :class_name => "Paragraph" def response_type case self.type - when "Response::SingleTextbox" + when "SingleTextbox" "single_textbox" else "question" @@ -452,21 +453,21 @@ def response_type def response_type=type self.type = case type when "single_textbox" - "Response::SingleTextbox" + "SingleTextbox" else "Response" end end end -class Response::SingleTextbox < Response - has_one :paragraph, :class_name => "ResponseText::Paragraph", :foreign_key => :response_id +class SingleTextbox < Response + has_one :paragraph, :class_name => "Paragraph", :foreign_key => :response_id end class ResponseText < ActiveRecord::Base end -class ResponseText::Paragraph < ResponseText +class Paragraph < ResponseText end class Person < ActiveRecord::Base @@ -478,6 +479,7 @@ class Person < ActiveRecord::Base belongs_to :preferences belongs_to :hair_cut has_one :author_detail + belongs_to :favorite_vehicle, class_name: 'Vehicle' has_and_belongs_to_many :books, join_table: :book_authors has_and_belongs_to_many :not_banned_books, -> { merge(Book.not_banned) }, @@ -1265,7 +1267,7 @@ def responses=params (datum[:relationships] || {}).each_pair { |k,v| case k when "paragraph" - response.paragraph = ResponseText::Paragraph.create(((v[:data][:attributes].respond_to?(:permit))? v[:data][:attributes].permit(:text) : v[:data][:attributes])) + response.paragraph = Paragraph.create(((v[:data][:attributes].respond_to?(:permit))? v[:data][:attributes].permit(:text) : v[:data][:attributes])) end } } @@ -1285,8 +1287,6 @@ def fetchable_fields end class ResponseResource < JSONAPI::Resource - model_hint model: Response::SingleTextbox, resource: :response - has_one :session attributes :question_id, :response_type @@ -1294,8 +1294,11 @@ class ResponseResource < JSONAPI::Resource has_one :paragraph end +class SingleTextboxResource < ResponseResource +end + class ParagraphResource < JSONAPI::Resource - model_name 'ResponseText::Paragraph' + model_name 'Paragraph' attributes :text @@ -1308,8 +1311,9 @@ class PersonResource < BaseResource has_many :comments, inverse_relationship: :author has_many :posts, inverse_relationship: :author - has_many :vehicles, polymorphic: true + has_many :vehicles, sti: true + has_one :favorite_vehicle, class_name: 'Vehicle', sti: true has_one :preferences has_one :hair_cut @@ -1357,12 +1361,10 @@ class VehicleResource < JSONAPI::Resource end class CarResource < VehicleResource - model_name "Car" attributes :drive_layout end class BoatResource < VehicleResource - model_name "Boat" attributes :length_at_water_line end @@ -2167,7 +2169,7 @@ class CommentResource < CommentResource; end class ExpenseEntryResource < ExpenseEntryResource; end class IsoCurrencyResource < IsoCurrencyResource; end class EmployeeResource < EmployeeResource; end - class VehicleResource < PersonResource; end + class VehicleResource < VehicleResource; end class HairCutResource < HairCutResource; end end end diff --git a/test/fixtures/people.yml b/test/fixtures/people.yml index 8bb10b780..41eed2ac8 100644 --- a/test/fixtures/people.yml +++ b/test/fixtures/people.yml @@ -31,6 +31,7 @@ e: date_joined: <%= DateTime.parse('2013-11-30 4:20:00 UTC +00:00') %> book_admin: true preferences_id: 55 + favorite_vehicle_id: 1 x: id: 1000 diff --git a/test/integration/requests/request_test.rb b/test/integration/requests/request_test.rb index 1863b5c7d..c7f0b0288 100644 --- a/test/integration/requests/request_test.rb +++ b/test/integration/requests/request_test.rb @@ -317,7 +317,7 @@ def test_post_single assert_jsonapi_response 201 end - def test_post_polymorphic_with_has_many_relationship + def test_post_sti_with_has_many_relationship post '/people', params: { 'data' => { @@ -380,7 +380,7 @@ def test_post_polymorphic_invalid_with_wrong_type assert_jsonapi_response 400, msg: "Submitting a thing as a vehicle should raise a type mismatch error" end - def test_post_polymorphic_invalid_with_not_matched_type_and_id + def test_post_sti_invalid_with_not_matched_type_and_id post '/people', params: { 'data' => { @@ -405,7 +405,7 @@ def test_post_polymorphic_invalid_with_not_matched_type_and_id 'Accept' => JSONAPI::MEDIA_TYPE } - assert_jsonapi_response 404, msg: "Submitting a thing as a vehicle should raise a record not found" + assert_jsonapi_response 404, msg: "Submitting a vehicle should raise a record not found if the type does not match" end def test_post_single_missing_data_contents @@ -680,7 +680,7 @@ def test_patch_polymorphic_invalid_with_wrong_type assert_jsonapi_response 400, msg: "Submitting a thing as a vehicle should raise a type mismatch error" end - def test_patch_polymorphic_invalid_with_not_matched_type_and_id + def test_patch_sti_invalid_with_not_matched_type_and_id patch '/people/1000', params: { 'data' => { diff --git a/test/unit/serializer/serializer_test.rb b/test/unit/serializer/serializer_test.rb index b3cda28e8..b7ac25935 100644 --- a/test/unit/serializer/serializer_test.rb +++ b/test/unit/serializer/serializer_test.rb @@ -323,6 +323,12 @@ def test_serializer_include self: '/people/1001/relationships/expenseEntries', related: '/people/1001/expenseEntries' } + }, + favoriteVehicle: { + links: { + self: '/people/1001/relationships/favoriteVehicle', + related: '/people/1001/favoriteVehicle' + } } } } @@ -455,6 +461,12 @@ def test_serializer_key_format self: '/people/1001/relationships/expenseEntries', related: '/people/1001/expenseEntries' } + }, + favorite_vehicle: { + links: { + self: '/people/1001/relationships/favoriteVehicle', + related: '/people/1001/favoriteVehicle' + } } } } From 51b916ff764cee622da03f0bc4deb14b012bf50e Mon Sep 17 00:00:00 2001 From: Larry Gebhardt Date: Tue, 23 Mar 2021 09:40:09 -0400 Subject: [PATCH 2/2] Quote table and alias names in sql --- lib/jsonapi/active_relation_resource.rb | 6 +++--- .../active_relation_resource_finder/join_manager_test.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/jsonapi/active_relation_resource.rb b/lib/jsonapi/active_relation_resource.rb index 89a66f84e..9c60e5ab8 100644 --- a/lib/jsonapi/active_relation_resource.rb +++ b/lib/jsonapi/active_relation_resource.rb @@ -151,13 +151,13 @@ def find_fragments(filters, options = {}) linkage_fields << {relationship_name: name, linkage_relationship: linkage_relationship, resource_klass: klass, - field: "#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}", + field: "#{concat_table_field(linkage_table_alias, primary_key)} AS \"#{linkage_table_alias}_#{primary_key}\"", alias: "#{linkage_table_alias}_#{primary_key}"} - pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}") + pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS \"#{linkage_table_alias}_#{primary_key}\"") if linkage_relationship.sti? - pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, 'type')} AS #{linkage_table_alias}_type") + pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, 'type')} AS \"#{linkage_table_alias}_type\"") end end end diff --git a/test/unit/active_relation_resource_finder/join_manager_test.rb b/test/unit/active_relation_resource_finder/join_manager_test.rb index a1198bf28..b0449e266 100644 --- a/test/unit/active_relation_resource_finder/join_manager_test.rb +++ b/test/unit/active_relation_resource_finder/join_manager_test.rb @@ -111,7 +111,7 @@ def test_add_nested_scoped_joins records = join_manager.join(records, {}) if (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 6 - sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" author ON author."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "author" ON "author"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true else sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true end @@ -137,7 +137,7 @@ def test_add_nested_scoped_joins # Note sql is in different order, but aliases should still be right if (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 6 - sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "people" author ON author."id" = "posts"."author_id" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "people" "author" ON "author"."id" = "posts"."author_id" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true else sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true end @@ -180,7 +180,7 @@ def test_add_nested_joins_with_fields records = join_manager.join(records, {}) if (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 6 - sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" author ON author."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "author" ON "author"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true else sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true end @@ -203,7 +203,7 @@ def test_add_joins_with_sub_relationship records = join_manager.join(records, {}) if (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 6 - sql = 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" author ON author."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" LEFT OUTER JOIN "comments" "comments_people" ON "comments_people"."author_id" = "people"."id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + sql = 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "author" ON "author"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" LEFT OUTER JOIN "comments" "comments_people" ON "comments_people"."author_id" = "people"."id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true assert_hash_equals({alias: 'author', join_type: :left}, join_manager.join_details_by_relationship(Api::V10::CommentResource._relationship(:author))) else sql = 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" LEFT OUTER JOIN "comments" "comments_people" ON "comments_people"."author_id" = "people"."id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true