'use strict'; // Generalizes `products` from Course-only (`course_id`) to a polymorphic // target (`purchasable_type` + `purchasable_id`) so standalone Units and // Lessons can also be sold individually, alongside plan-tier gating — see // utils/purchasable.util.js for the resolver used by every consumer. // // purchasable_type is STRING + explicit CHECK, not a real ENUM (CockroachDB // can't create a new enum type via addColumn the way createTable can — same // caveat as 20270101000076-add-assets-transcode-status.js). // // Dropping `course_id` also drops its FK (`onDelete: 'CASCADE'` from // 20270101000019-create-products.js) — referential integrity across the three // possible targets is now enforced at the application layer only, same as // course_purchases.product_id already is today. // // Touches a table with live production rows (real courses have real // products/course_purchases). NOT auto-run — test against a local/staging // copy of the DB first; do not execute against the shared dev/prod database // without explicit confirmation. module.exports = { async up(queryInterface, Sequelize) { await queryInterface.addColumn('products', 'purchasable_type', { type: Sequelize.STRING(10), allowNull: false, defaultValue: 'course', }); await queryInterface.addConstraint('products', { fields: ['purchasable_type'], type: 'check', name: 'check_products_purchasable_type', where: { purchasable_type: { [Sequelize.Op.in]: ['course', 'unit', 'lesson'] } }, }); await queryInterface.addColumn('products', 'purchasable_id', { type: Sequelize.BIGINT, allowNull: true, // filled by the backfill below, then locked to NOT NULL }); await queryInterface.sequelize.query(` UPDATE products SET purchasable_type = 'course', purchasable_id = course_id; `); await queryInterface.changeColumn('products', 'purchasable_id', { type: Sequelize.BIGINT, allowNull: false, }); await queryInterface.removeIndex('products', ['course_id']); await queryInterface.removeColumn('products', 'course_id'); await queryInterface.addIndex('products', ['purchasable_type', 'purchasable_id'], { name: 'products_purchasable_type_id', }); }, async down(queryInterface, Sequelize) { await queryInterface.removeIndex('products', 'products_purchasable_type_id'); await queryInterface.addColumn('products', 'course_id', { type: Sequelize.BIGINT, allowNull: true, }); await queryInterface.sequelize.query(` UPDATE products SET course_id = purchasable_id WHERE purchasable_type = 'course'; `); await queryInterface.changeColumn('products', 'course_id', { type: Sequelize.BIGINT, allowNull: false, }); await queryInterface.addIndex('products', ['course_id']); await queryInterface.removeColumn('products', 'purchasable_id'); await queryInterface.removeConstraint('products', 'check_products_purchasable_type'); await queryInterface.removeColumn('products', 'purchasable_type'); }, };