From 081ec72732f050319e4eaec78b8b88d52664ea4a Mon Sep 17 00:00:00 2001 From: Andrew Heuermann Date: Mon, 30 Dec 2019 21:49:02 -0600 Subject: [PATCH] fix: fixes gets on many-to-many with non-primary target key #11587 --- lib/associations/belongs-to-many.js | 3 + .../associations/belongs-to-many.test.js | 79 +++++++++++++++++++ .../unit/associations/belongs-to-many.test.js | 41 +++++++++- 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/lib/associations/belongs-to-many.js b/lib/associations/belongs-to-many.js index 9fcbe758b627..54dbaf8b1b68 100644 --- a/lib/associations/belongs-to-many.js +++ b/lib/associations/belongs-to-many.js @@ -355,6 +355,7 @@ class BelongsToMany extends Association { }); this.oneFromSource = new HasOne(this.source, this.through.model, { foreignKey: this.foreignKey, + sourceKey: this.sourceKey, as: this.through.model.name }); @@ -366,6 +367,7 @@ class BelongsToMany extends Association { }); this.oneFromTarget = new HasOne(this.target, this.through.model, { foreignKey: this.otherKey, + sourceKey: this.targetKey, as: this.through.model.name }); @@ -376,6 +378,7 @@ class BelongsToMany extends Association { this.paired.oneFromTarget = new HasOne(this.paired.target, this.paired.through.model, { foreignKey: this.paired.otherKey, + sourceKey: this.paired.targetKey, as: this.paired.through.model.name }); } diff --git a/test/integration/associations/belongs-to-many.test.js b/test/integration/associations/belongs-to-many.test.js index 84449e021577..bd3dcf3c6264 100644 --- a/test/integration/associations/belongs-to-many.test.js +++ b/test/integration/associations/belongs-to-many.test.js @@ -731,6 +731,85 @@ describe(Support.getTestDialectTeaser('BelongsToMany'), () => { }); }); + it('supports non primary key attributes for joins for getting associations (sourceKey/targetKey)', function() { + const User = this.sequelize.define('User', { + userId: { + type: DataTypes.UUID, + allowNull: false, + primaryKey: true, + defaultValue: DataTypes.UUIDV4 + }, + userSecondId: { + type: DataTypes.UUID, + allowNull: false, + defaultValue: DataTypes.UUIDV4, + field: 'user_second_id' + } + }, { + tableName: 'tbl_user', + indexes: [ + { + unique: true, + fields: ['user_second_id'] + } + ] + }); + + const Group = this.sequelize.define('Group', { + groupId: { + type: DataTypes.UUID, + allowNull: false, + primaryKey: true, + defaultValue: DataTypes.UUIDV4 + }, + groupSecondId: { + type: DataTypes.UUID, + allowNull: false, + defaultValue: DataTypes.UUIDV4, + field: 'group_second_id' + } + }, { + tableName: 'tbl_group', + indexes: [ + { + unique: true, + fields: ['group_second_id'] + } + ] + }); + + User.belongsToMany(Group, { through: 'usergroups', sourceKey: 'userSecondId', targetKey: 'groupSecondId' }); + Group.belongsToMany(User, { through: 'usergroups', sourceKey: 'groupSecondId', targetKey: 'userSecondId' }); + + return this.sequelize.sync({ force: true }).then(() => { + return Promise.join( + User.create(), + User.create(), + Group.create(), + Group.create() + ).then(([user1, user2, group1, group2]) => { + return Promise.join(user1.addGroup(group1), user2.addGroup(group2)) + .then(() => { + return Promise.join( + user1.getGroups(), + user2.getGroups(), + group1.getUsers(), + group2.getUsers() + ); + }).then(([groups1, groups2, users1, users2]) => { + expect(groups1.length).to.be.equal(1); + expect(groups1[0].id).to.be.equal(group1.id); + expect(groups2.length).to.be.equal(1); + expect(groups2[0].id).to.be.equal(group2.id); + expect(users1.length).to.be.equal(1); + expect(users1[0].id).to.be.equal(user1.id); + expect(users2.length).to.be.equal(1); + expect(users2[0].id).to.be.equal(user2.id); + }); + }); + }); + }); + it('supports non primary key attributes for joins (custom foreignKey)', function() { const User = this.sequelize.define('User', { id: { diff --git a/test/unit/associations/belongs-to-many.test.js b/test/unit/associations/belongs-to-many.test.js index 672187f800f1..1b621a6c4268 100644 --- a/test/unit/associations/belongs-to-many.test.js +++ b/test/unit/associations/belongs-to-many.test.js @@ -332,7 +332,7 @@ describe(Support.getTestDialectTeaser('belongsToMany'), () => { expect(Object.keys(ProductTag.rawAttributes)).to.deep.equal(['id', 'priority', 'productId', 'tagId']); }); - it('should setup hasOne relations to source and target from join model with defined foreign/other keys', function() { + it('should setup hasMany relations to source and target from join model with defined foreign/other keys', function() { const Product = this.sequelize.define('Product', { title: DataTypes.STRING }), @@ -406,6 +406,45 @@ describe(Support.getTestDialectTeaser('belongsToMany'), () => { expect(Object.keys(ProductTag.rawAttributes)).to.deep.equal(['id', 'priority', 'productId', 'tagId']); }); + it('should setup hasOne relations to source and target from join model with defined source keys', function() { + const Product = this.sequelize.define('Product', { + title: DataTypes.STRING, + productSecondaryId: DataTypes.STRING + }), + Tag = this.sequelize.define('Tag', { + name: DataTypes.STRING, + tagSecondaryId: DataTypes.STRING + }), + ProductTag = this.sequelize.define('ProductTag', { + id: { + primaryKey: true, + type: DataTypes.INTEGER, + autoIncrement: true + }, + priority: DataTypes.INTEGER + }, { + timestamps: false + }); + + Product.Tags = Product.belongsToMany(Tag, { through: ProductTag, sourceKey: 'productSecondaryId' }); + Tag.Products = Tag.belongsToMany(Product, { through: ProductTag, sourceKey: 'tagSecondaryId' }); + + expect(Product.Tags.oneFromSource).to.be.an.instanceOf(HasOne); + expect(Product.Tags.oneFromTarget).to.be.an.instanceOf(HasOne); + + expect(Tag.Products.oneFromSource).to.be.an.instanceOf(HasOne); + expect(Tag.Products.oneFromTarget).to.be.an.instanceOf(HasOne); + + expect(Tag.Products.oneFromSource.sourceKey).to.equal(Tag.Products.sourceKey); + expect(Tag.Products.oneFromTarget.sourceKey).to.equal(Tag.Products.targetKey); + + expect(Product.Tags.oneFromSource.sourceKey).to.equal(Product.Tags.sourceKey); + expect(Product.Tags.oneFromTarget.sourceKey).to.equal(Product.Tags.targetKey); + + expect(Object.keys(ProductTag.rawAttributes).length).to.equal(4); + expect(Object.keys(ProductTag.rawAttributes)).to.deep.equal(['id', 'priority', 'ProductProductSecondaryId', 'TagTagSecondaryId']); + }); + it('should setup belongsTo relations to source and target from join model with only foreign keys defined', function() { const Product = this.sequelize.define('Product', { title: DataTypes.STRING