diff --git a/src/database/migrations/20251003155747-add-feature-role-mapping-constraints.js b/src/database/migrations/20251003155747-add-feature-role-mapping-constraints.js index 3157f3fb..2d82cd93 100644 --- a/src/database/migrations/20251003155747-add-feature-role-mapping-constraints.js +++ b/src/database/migrations/20251003155747-add-feature-role-mapping-constraints.js @@ -3,77 +3,59 @@ /** @type {import('sequelize-cli').Migration} */ module.exports = { async up(queryInterface, Sequelize) { - // Add foreign key constraint for feature_code - await queryInterface.addConstraint('feature_role_mapping', { - fields: ['feature_code'], - type: 'foreign key', - name: 'fk_feature_role_mapping_feature_code', - references: { - table: 'features', - field: 'code', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }) - - // Add foreign key constraint for tenant_code - await queryInterface.addConstraint('feature_role_mapping', { - fields: ['tenant_code'], - type: 'foreign key', - name: 'fk_feature_role_mapping_tenant_code', - references: { - table: 'tenants', - field: 'code', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }) - - // Add composite foreign key for organization_code (organization_code, tenant_code) -> organizations (code, tenant_code) - await queryInterface.sequelize.query(` - ALTER TABLE feature_role_mapping - ADD CONSTRAINT fk_feature_role_mapping_organization_code - FOREIGN KEY (organization_code, tenant_code) - REFERENCES organizations (code, tenant_code) - ON UPDATE CASCADE - ON DELETE CASCADE; - `) - - // Add composite foreign key for role_title (tenant_code, role_title) -> user_roles (tenant_code, title) - //commenting this as of now because of issue in user_roles table which is using the organization_id as foreign key - // await queryInterface.sequelize.query(` - // ALTER TABLE feature_role_mapping - // ADD CONSTRAINT fk_feature_role_mapping_role_title - // FOREIGN KEY (tenant_code, role_title) - // REFERENCES user_roles (tenant_code, title) - // ON UPDATE CASCADE - // ON DELETE NO ACTION; - // `) - - // Unique constraint for feature_code, role_title, organization_code, tenant_code - await queryInterface.sequelize.query(` - CREATE UNIQUE INDEX feature_role_org_tenant_unique - ON feature_role_mapping (feature_code, role_title, organization_code, tenant_code) - WHERE deleted_at IS NULL; - `) - - await queryInterface.sequelize.query(` - ALTER TABLE feature_role_mapping - ADD CONSTRAINT fk_org_feature_role_mapping_organization_code - FOREIGN KEY (feature_code, tenant_code, organization_code) - REFERENCES organization_features (feature_code, tenant_code, organization_code) - ON UPDATE CASCADE - ON DELETE CASCADE; - `) + try { + await queryInterface.addConstraint('feature_role_mapping', { + fields: ['feature_code'], + type: 'foreign key', + name: 'fk_feature_role_mapping_feature_code', + references: { table: 'features', field: 'code' }, + onUpdate: 'NO ACTION', + onDelete: 'NO ACTION', + }) + // Composite FK: (organization_code, tenant_code) → organizations(code, tenant_code) + await queryInterface.sequelize.query(` + ALTER TABLE feature_role_mapping + ADD CONSTRAINT fk_feature_role_mapping_organization_code + FOREIGN KEY (organization_code, tenant_code) + REFERENCES organizations (code, tenant_code) + ON UPDATE CASCADE + ON DELETE CASCADE; + `) + + // Unique index ignoring soft-deleted rows + await queryInterface.sequelize.query(` + CREATE UNIQUE INDEX feature_role_org_tenant_unique + ON feature_role_mapping (feature_code, role_title, organization_code, tenant_code) + WHERE deleted_at IS NULL; + `) + + // Composite FK: (feature_code, tenant_code, organization_code) → organization_features(...) + await queryInterface.sequelize.query(` + ALTER TABLE feature_role_mapping + ADD CONSTRAINT fk_org_feature_role_mapping_organization_code + FOREIGN KEY (feature_code, tenant_code, organization_code) + REFERENCES organization_features (feature_code, tenant_code, organization_code) + ON UPDATE CASCADE + ON DELETE CASCADE; + `) + } catch (error) { + console.error('Migration failed:', error) + throw error // important so sequelize knows migration failed + } }, async down(queryInterface, Sequelize) { - // Drop foreign key constraints - await queryInterface.removeConstraint('feature_role_mapping', 'fk_feature_role_mapping_tenant_code') - await queryInterface.removeConstraint('feature_role_mapping', 'fk_feature_role_mapping_organization_code') + // Remove constraints safely + await queryInterface + .removeConstraint('feature_role_mapping', 'fk_feature_role_mapping_organization_code') + .catch(() => {}) // await queryInterface.removeConstraint('feature_role_mapping', 'fk_feature_role_mapping_role_title') - await queryInterface.removeConstraint('feature_role_mapping', 'fk_feature_role_mapping_feature_code') - await queryInterface.removeConstraint('feature_role_mapping', 'fk_org_feature_role_mapping_organization_code') + await queryInterface + .removeConstraint('feature_role_mapping', 'fk_feature_role_mapping_feature_code') + .catch(() => {}) + await queryInterface + .removeConstraint('feature_role_mapping', 'fk_org_feature_role_mapping_organization_code') + .catch(() => {}) await queryInterface.sequelize.query('DROP INDEX IF EXISTS feature_role_org_tenant_unique;') }, diff --git a/src/database/migrations/20251211000000-modify-feature-role-mapping-fk-constraints.js b/src/database/migrations/20251211000000-modify-feature-role-mapping-fk-constraints.js new file mode 100644 index 00000000..95c3d5ba --- /dev/null +++ b/src/database/migrations/20251211000000-modify-feature-role-mapping-fk-constraints.js @@ -0,0 +1,119 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface) { + try { + console.log('Removing existing foreign key constraints...') + + // Remove the existing FK constraint: feature_code → features(code) + // This constraint is incompatible with Citus distributed database and is being permanently removed + await queryInterface.sequelize + .query( + ` + ALTER TABLE feature_role_mapping + DROP CONSTRAINT IF EXISTS fk_feature_role_mapping_feature_code; + ` + ) + .catch(() => { + console.warn( + 'Warning: fk_feature_role_mapping_feature_code constraint not found or already removed' + ) + }) + + // Remove the existing composite FK: (organization_code, tenant_code) → organizations + await queryInterface.sequelize + .query( + ` + ALTER TABLE feature_role_mapping + DROP CONSTRAINT IF EXISTS fk_feature_role_mapping_organization_code; + ` + ) + .catch(() => { + console.warn( + 'Warning: fk_feature_role_mapping_organization_code constraint not found or already removed' + ) + }) + + // Remove the existing composite FK: (feature_code, tenant_code, organization_code) → organization_features + await queryInterface.sequelize + .query( + ` + ALTER TABLE feature_role_mapping + DROP CONSTRAINT IF EXISTS fk_org_feature_role_mapping_organization_code; + ` + ) + .catch(() => { + console.warn( + 'Warning: fk_org_feature_role_mapping_organization_code constraint not found or already removed' + ) + }) + + console.log('Creating new foreign key constraints with NO ACTION...') + + // Recreate composite FK: (organization_code, tenant_code) → organizations with NO ACTION + await queryInterface.sequelize.query(` + ALTER TABLE feature_role_mapping + ADD CONSTRAINT fk_feature_role_mapping_organization_code + FOREIGN KEY (organization_code, tenant_code) + REFERENCES organizations (code, tenant_code) + ON UPDATE NO ACTION + ON DELETE NO ACTION; + `) + + // Recreate composite FK: (feature_code, tenant_code, organization_code) → organization_features with NO ACTION + await queryInterface.sequelize.query(` + ALTER TABLE feature_role_mapping + ADD CONSTRAINT fk_org_feature_role_mapping_organization_code + FOREIGN KEY (feature_code, tenant_code, organization_code) + REFERENCES organization_features (feature_code, tenant_code, organization_code) + ON UPDATE NO ACTION + ON DELETE NO ACTION; + `) + + console.log('Foreign key constraints successfully modified to NO ACTION') + } catch (error) { + console.error('Migration failed:', error) + throw error + } + }, + + async down(queryInterface) { + try { + console.log('Removing NO ACTION constraints (manual recreation of CASCADE constraints may be needed)...') + + // Remove NO ACTION constraints + await queryInterface.sequelize + .query( + ` + ALTER TABLE feature_role_mapping + DROP CONSTRAINT IF EXISTS fk_feature_role_mapping_feature_code; + ` + ) + .catch(() => {}) + + await queryInterface.sequelize + .query( + ` + ALTER TABLE feature_role_mapping + DROP CONSTRAINT IF EXISTS fk_feature_role_mapping_organization_code; + ` + ) + .catch(() => {}) + + await queryInterface.sequelize + .query( + ` + ALTER TABLE feature_role_mapping + DROP CONSTRAINT IF EXISTS fk_org_feature_role_mapping_organization_code; + ` + ) + .catch(() => {}) + + console.log('Constraints removed. Run previous migration to restore CASCADE constraints.') + } catch (error) { + console.error('Rollback failed:', error) + throw error + } + }, +} diff --git a/src/database/models/Feature.js b/src/database/models/Feature.js index a5d20a87..e098c428 100644 --- a/src/database/models/Feature.js +++ b/src/database/models/Feature.js @@ -62,11 +62,6 @@ module.exports = (sequelize, DataTypes) => { foreignKey: 'feature_code', as: 'organization_features', }) - Feature.hasMany(models.FeatureRoleMapping, { - foreignKey: 'feature_code', - sourceKey: 'code', - as: 'featureRoleMappings', - }) } return Feature diff --git a/src/services/account.js b/src/services/account.js index 88d9b71e..5bb25a69 100644 --- a/src/services/account.js +++ b/src/services/account.js @@ -53,7 +53,6 @@ module.exports = class AccountHelper { * @param {Object} deviceInfo - Device information * @returns {JSON} - returns account creation details. */ - static async create(bodyData, deviceInfo, domain) { const projection = ['password'] let isInvitedUserId = false @@ -1048,7 +1047,7 @@ module.exports = class AccountHelper { responseCode: 'CLIENT_ERROR', }) } - const user = await userQueries.findUserWithOrganization(query, {}, tenantDomain.tenant_code) + const user = await userQueries.findUserWithOrganization(query, {}, true) if (!user) { return responses.failureResponse({