{"version":3,"file":"bidirectional.mjs","sources":["../../../src/validations/relations/bidirectional.ts"],"sourcesContent":["import { snakeCase } from 'lodash/fp';\nimport type { Database } from '../..';\nimport type { Relation } from '../../types';\nimport { identifiers } from '../../utils/identifiers';\n\ntype Link = {\n relation: Relation.Bidirectional & { inversedBy: string };\n invRelation: Relation.Bidirectional & { inversedBy: string };\n};\n\nconst getLinksWithoutMappedBy = (db: Database): Array => {\n const relationsToUpdate: Record = {};\n\n db.metadata.forEach((modelMetadata) => {\n const attributes = modelMetadata.attributes;\n\n // For each relation attribute, add the joinTable name to tablesToUpdate\n Object.values(attributes).forEach((attribute) => {\n if (attribute.type !== 'relation') {\n return;\n }\n\n if ('inversedBy' in attribute && attribute.inversedBy) {\n const invRelation = db.metadata.get(attribute.target).attributes[attribute.inversedBy];\n\n // Both relations use inversedBy.\n if ('inversedBy' in invRelation && invRelation.inversedBy) {\n relationsToUpdate[attribute.joinTable.name] = {\n relation: attribute as Relation.Bidirectional & { inversedBy: string },\n invRelation: invRelation as Relation.Bidirectional & { inversedBy: string },\n };\n }\n }\n });\n });\n\n return Object.values(relationsToUpdate);\n};\n\nconst isLinkTableEmpty = async (db: Database, linkTableName: string) => {\n // If the table doesn't exist, it's empty\n const exists = await db.getSchemaConnection().hasTable(linkTableName);\n if (!exists) return true;\n\n const result = await db.getConnection().from(linkTableName).count('* as count');\n return Number(result[0].count) === 0;\n};\n\n/**\n * Validates bidirectional relations before starting the server.\n * - If both sides use inversedBy, one of the sides must switch to mappedBy.\n * When this happens, two join tables exist in the database.\n * This makes sure you switch the side which does not delete any data.\n *\n * @param {*} db\n * @return {*}\n */\nexport const validateBidirectionalRelations = async (db: Database) => {\n const invalidLinks = getLinksWithoutMappedBy(db);\n\n for (const { relation, invRelation } of invalidLinks) {\n const modelMetadata = db.metadata.get(invRelation.target);\n const invModelMetadata = db.metadata.get(relation.target);\n\n // Generate the join table name based on the relation target table and attribute name.\n const joinTableName = identifiers.getJoinTableName(\n snakeCase(modelMetadata.tableName),\n snakeCase(invRelation.inversedBy)\n );\n const inverseJoinTableName = identifiers.getJoinTableName(\n snakeCase(invModelMetadata.tableName),\n snakeCase(relation.inversedBy)\n );\n\n const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);\n const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);\n\n if (joinTableEmpty) {\n process.emitWarning(\n `Error on attribute \"${invRelation.inversedBy}\" in model \"${modelMetadata.singularName}\" (${modelMetadata.uid}).` +\n ` Please modify your ${modelMetadata.singularName} schema by renaming the key \"inversedBy\" to \"mappedBy\".` +\n ` Ex: { \"inversedBy\": \"${relation.inversedBy}\" } -> { \"mappedBy\": \"${relation.inversedBy}\" }`\n );\n } else if (inverseJoinTableEmpty) {\n // Its safe to delete the inverse join table\n process.emitWarning(\n `Error on attribute \"${relation.inversedBy}\" in model \"${invModelMetadata.singularName}\" (${invModelMetadata.uid}).` +\n ` Please modify your ${invModelMetadata.singularName} schema by renaming the key \"inversedBy\" to \"mappedBy\".` +\n ` Ex: { \"inversedBy\": \"${invRelation.inversedBy}\" } -> { \"mappedBy\": \"${invRelation.inversedBy}\" }`\n );\n } else {\n // Both sides have data in the join table\n }\n }\n};\n"],"names":["getLinksWithoutMappedBy","db","relationsToUpdate","metadata","forEach","modelMetadata","attributes","Object","values","attribute","type","inversedBy","invRelation","get","target","joinTable","name","relation","isLinkTableEmpty","linkTableName","exists","getSchemaConnection","hasTable","result","getConnection","from","count","Number","validateBidirectionalRelations","invalidLinks","invModelMetadata","joinTableName","identifiers","getJoinTableName","snakeCase","tableName","inverseJoinTableName","joinTableEmpty","inverseJoinTableEmpty","process","emitWarning","singularName","uid"],"mappings":"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"}