Sequelise : Many To Many table(CROSS TABLE) associated to other table

This is my Diagram DATABASE : https://i.stack.imgur.com/CGAwh.png

I made models of my databases with SEQUELIZE like that :

MODEL : Level

module.exports = (sequelize, DataTypes) => {
  const Level = sequelize.define(
    'Level',
    {
      level_id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      label: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: {
          args: true,
          msg: 'Level:Label already exist!',
        },
        validate: {
          notEmpty: { msg: `Level:Label cannot be empty!` },
          notNull: { msg: `Level:Label cannot be NULL!` },
        },
      },
      ref: {
        type: DataTypes.STRING,
        allowNull: true,
      },
      description: {
        type: DataTypes.TEXT,
        allowNull: true,
      },
    },
    {
      tableName: 'levels',
      timestamps: false,
    }
  );

  Level.associate = (models) => {
    Level.belongsToMany(models.Test, {
      through: models.testHasLevel,
      foreignKey: 'level_id',
      otherKey: 'test_id',
      timestamps: false,
    });
  };

  return Level;
};

Model : TEST :

    module.exports = (sequelize, DataTypes) => {
  const Test = sequelize.define(
    'Test',
    {
      test_id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      label: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
          notEmpty: { msg: `Test:label cannot be empty!` },
          notNull: { msg: `Test:label cannot be NULL!` },
        },
      },
      isInternal: {
        type: DataTypes.BOOLEAN,
        defaultValue: false,
        allowNull: false,
        validate: {
          notEmpty: { msg: `Test:isInternal cannot be empty!` },
          notNull: { msg: `Test:isInternal cannot be NULL!` },
        },
      },
      parent_id: {
        type: DataTypes.INTEGER,
        defaultValue: null,
        allowNull: true,
      },
    },
    {
      tableName: 'tests',
      timestamps: false,
    }
  );

  Test.associate = (models) => {
    Test.belongsToMany(models.Level, {
      through: models.testHasLevel,
      foreignKey: 'test_id',
      otherKey: 'level_id',
      timestamps: false,
    });
    Test.hasMany(models.Test, { foreignKey: 'parent_id', as: 'children' });
  };

  return Test;
};

MODEL : TEST HAS MODEL

    module.exports = (sequelize, DataTypes) => {
  const testHasLevel = sequelize.define(
    'testHasLevel',
    {},
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      tableName: 'test_has_level',
      timestamps: false,
    }
  );
  testHasLevel.associate = (models) => {
    testHasLevel.belongsTo(models.Test, {
      foreignKey: 'test_id',
      targetKey: 'test_id',
    });
    testHasLevel.belongsTo(models.Level, {
      foreignKey: 'level_id',
      targetKey: 'level_id',
    });
  };

  return testHasLevel;
};

I made also SESSION MODEL :

module.exports = (sequelize, DataTypes) => {
  const Session = sequelize.define(
    'Session',
    {
      session_id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      institut_id: {
        type: DataTypes.INTEGER,
      },
      start: {
        type: DataTypes.DATE,
      },
      end: {
        type: DataTypes.DATE,
      },
      test_id: {
        type: DataTypes.INTEGER,
      },
      level_id: {
        type: DataTypes.INTEGER,
      },
      limitDateSubscribe: {
        type: DataTypes.DATE,
      },
      placeAvailable: {
        type: DataTypes.INTEGER,
      },
    },
    {
      tableName: 'sessions',
      timestamps: false,
    }
  );
  Session.associate = (models) => {
    Session.hasMany(models.sessionHasUser, { foreignKey: 'session_id' });
  };
  return Session;
};

But i have no idea how to “BIND” SESSION with TEST_HAS_LEVEL with Sequelize …. What should i change ? cause i know “composite key” are not allowed with the last version of sequelize.

In other term : How associate properly a cross table with a one to many relationship to an other table ?

Answer

Model: Level

module.exports = (sequelize, DataTypes) => {
  const Level = sequelize.define(
    "Level",
    {
      level_id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      label: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: {
          args: true,
          msg: "Level:Label already exist!",
        },
        validate: {
          notEmpty: { msg: `Level:Label cannot be empty!` },
          notNull: { msg: `Level:Label cannot be NULL!` },
        },
      },
      ref: {
        type: DataTypes.STRING,
        allowNull: true,
      },
      description: {
        type: DataTypes.TEXT,
        allowNull: true,
      },
    },
    {
      tableName: "levels",
      timestamps: false,
    }
  );

  Level.associate = (models) => {
    Level.hasMany(models.testHasLevel, {
      foreignKey: "level_level_id",
      as: "levels",
    });
  };

  return Level;
};

Model: Test

module.exports = (sequelize, DataTypes) => {
  const Test = sequelize.define(
    "Test",
    {
      test_id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      label: {
        type: DataTypes.STRING,
        allowNull: false,
        validate: {
          notEmpty: { msg: `Test:label cannot be empty!` },
          notNull: { msg: `Test:label cannot be NULL!` },
        },
      },
      isInternal: {
        type: DataTypes.BOOLEAN,
        defaultValue: false,
        allowNull: false,
        validate: {
          notEmpty: { msg: `Test:isInternal cannot be empty!` },
          notNull: { msg: `Test:isInternal cannot be NULL!` },
        },
      },
      parent_id: {
        type: DataTypes.INTEGER,
        defaultValue: null,
        allowNull: true,
      },
    },
    {
      tableName: "tests",
      timestamps: false,
    }
  );

  Test.associate = (models) => {
    Test.hasMany(models.testHasLevel, {
      foreignKey: "test_test_id",
      as: "tests",
    });
    Test.hasMany(models.Test, { foreignKey: "parent_id", as: "children" });
  };

  return Test;
};

Model: Test has level

module.exports = (sequelize, DataTypes) => {
  const testHasLevel = sequelize.define(
    "testHasLevel",
    {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      test_test_id: {
        type: DataTypes.INTEGER,
      },
      level_level_id: {
        type: DataTypes.INTEGER,
      },
    },
    {
      tableName: "test_has_level",
      timestamps: false,
    }
  );
  testHasLevel.associate = (models) => {
    testHasLevel.belongsTo(models.Test, {
      foreignKey: "test_test_id",
      as: "tests",
    });
    testHasLevel.belongsTo(models.Level, {
      foreignKey: "level_level_id",
      as: "levels",
    });
    testHasLevel.hasMany(models.Session, {
      foreignKey: "test_has_level_id",
      as: "test_has_level",
    });
  };

  return testHasLevel;
};

Model: Session

module.exports = (sequelize, DataTypes) => {
  const Session = sequelize.define(
    "Session",
    {
      session_id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
      },
      institut_id: {
        type: DataTypes.INTEGER,
      },
      start: {
        type: DataTypes.DATE,
      },
      end: {
        type: DataTypes.DATE,
      },
      test_has_level_id: {
        type: DataTypes.INTEGER,
      },
      limitDateSubscribe: {
        type: DataTypes.DATE,
      },
      placeAvailable: {
        type: DataTypes.INTEGER,
      },
    },
    {
      tableName: "sessions",
      timestamps: false,
    }
  );
  Session.associate = (models) => {
    Session.belongsTo(models.testHasLevel, {
      foreignKey: "test_has_level_id",
      as: "test_has_level",
    });
  };
  return Session;
};