'Sequelize BelongsToManyAddAssociationMixin performing update instead of insert in polymorphic table

I'm having a problem within my application that is related to multiple tables/models arranged in a Many:Many relationship but also leverages polymorphic columns and is using the BelongsToManyAddAssociationMixin. I am unable to insert multiple records in a M:N relationship because I'm unable to tell Sequelize to change the unique fields to include roleableId

Essentially I have a User model, a Role model, a relationship table called UserRole and other models which are "roleable" (For the sake of this example, one model is a Facility)

So essentially my UserRole model looks like

export class UserRole extends Model {
  public readonly id!: UUID
  public RoleId!: UUID
  public UserId!: UUID
  public roleableType!: string
  public roleableId!: string

  public readonly User!: User
  public readonly Role!: Role

  public getUser!: BelongsToGetAssociationMixin<User>
  public getRole!: BelongsToGetAssociationMixin<Role>
}

export const initializeUserRole = (sequelize: Sequelize) => {
  UserRole.init(
    {
      RoleId: {
        type: DataTypes.UUID,
        allowNull: false,
        validate: {
          isUUID: 4,
        },
        field: 'role_id',
      },
      UserId: {
        type: DataTypes.UUID,
        allowNull: false,
        validate: {
          isUUID: 4,
        },
        field: 'user_id',
      },
      roleableId: {
        type: DataTypes.UUID,
        field: 'roleable_id',
        validate: {
          isUUID: 4,
        },
      },
      roleableType: {
        type: DataTypes.STRING,
        field: 'roleable_type',
      },
    },
    {
      timestamps: false,
      sequelize,
      tableName: TableNames.USER_ROLES,
    }
  )
}

In my scenario, I would have a role named Sales Rep and it's primary key is a UUID

In theory, I would like to create a new record for the user to have the SalesRep role twice in my UserRole table, one with values for roleableId & roleableType and one where those values are null.

Using mixins, I'm able to add these roles with something like

const randomUser = await userFactory()
const salesRepRole = await roleFactory({ roleName: 'Sales Rep' })

await randomUser.addRole(salesRepRole)

^^^ This works properly

But if I try to do something like:

const randomUser = await userFactory()
const randomFacility = await facilityFactory()
const salesRepRole = await roleFactory({ roleName: 'Sales Rep' })

await randomUser.addRole(salesRepRole)
await randomuser.addRole(salesRepRole, { through: { roleableId: randomFacility.id, roleableType: 'facility' }})

What will happen is that instead of performing a new insert into my UserRole table, instead Sequelize will identify that there is already a record based upon User.id & Role.id and will perform an UPDATE instead, resulting in only one record being returned.

I'm not sure how to indicate to Sequelize that instead of putting a unique constraint on User.id & Role.id, I really need it to be User.id, Role.id & roleableId

My work around right now has been to simply just roll my own add<Model> function which will perform an INSERT properly (adding an index on my DB to check for unique instances of user_id,role_id,roleable that throw an error if this combination already exists) but this feels a bit dirty and I'd rather try to utilize the mixin as much as possible, especially if I also have to add a BelongsToManyAddAssociationsMixin version as well for addRoles

Does anyone have any ideas? I'm happy to elaborate more if that helps.

Thanks!



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source