{"version":3,"file":"clean-component-join-table.mjs","sources":["../../../../src/services/document-service/utils/clean-component-join-table.ts"],"sourcesContent":["import type { Database } from '@strapi/database';\nimport type { Schema } from '@strapi/types';\nimport { findComponentParent, getParentSchemasForComponent } from '../components';\n\n/**\n * Cleans ghost relations with publication state mismatches from a join table\n * Uses schema-based draft/publish checking like prevention fix\n */\nexport const cleanComponentJoinTable = async (\n db: Database,\n joinTableName: string,\n relation: any,\n sourceModel: any\n): Promise => {\n try {\n // Get the target model metadata\n const targetModel = db.metadata.get(relation.target);\n if (!targetModel) {\n db.logger.debug(`Target model ${relation.target} not found, skipping ${joinTableName}`);\n return 0;\n }\n\n // Check if source supports draft/publish, if it doesnt it should contain duplicate states\n const sourceContentType = strapi.contentTypes[sourceModel.uid];\n // It could be a model, which does not have the draftAndPublish option\n const sourceSupportsDraftPublish = sourceContentType?.options?.draftAndPublish;\n\n if (sourceContentType && !sourceSupportsDraftPublish) {\n return 0;\n }\n\n // Check if target supports draft/publish using schema-based approach (like prevention fix)\n const targetContentType =\n strapi.contentTypes[relation.target as keyof typeof strapi.contentTypes];\n const targetSupportsDraftPublish = targetContentType?.options?.draftAndPublish || false;\n\n if (!targetSupportsDraftPublish) {\n return 0;\n }\n\n // Find entries with publication state mismatches\n const ghostEntries = await findPublicationStateMismatches(\n db,\n joinTableName,\n relation,\n targetModel,\n sourceModel\n );\n\n if (ghostEntries.length === 0) {\n return 0;\n }\n\n // Remove ghost entries\n await db.connection(joinTableName).whereIn('id', ghostEntries).del();\n db.logger.debug(\n `Removed ${ghostEntries.length} ghost relations with publication state mismatches from ${joinTableName}`\n );\n\n return ghostEntries.length;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n db.logger.error(`Failed to clean join table \"${joinTableName}\": ${errorMessage}`);\n return 0;\n }\n};\n\nconst findContentTypeParentForComponentInstance = async (\n componentSchema: Schema.Component,\n componentId: number | string\n): Promise<{ uid: string; table: string; parentId: number | string } | null> => {\n // Get the parent schemas that could contain this component\n const parentSchemas = getParentSchemasForComponent(componentSchema);\n if (parentSchemas.length === 0) {\n // No potential parents\n return null;\n }\n\n // Find the actual parent for THIS specific component instance\n const parent = await findComponentParent(componentSchema, componentId, parentSchemas);\n if (!parent) {\n // No parent found for this component instance\n return null;\n }\n\n if (strapi.components[parent.uid as keyof typeof strapi.components]) {\n // If the parent is a component, we need to check its parents recursively\n const parentComponentSchema = strapi.components[parent.uid as keyof typeof strapi.components];\n return findContentTypeParentForComponentInstance(parentComponentSchema, parent.parentId);\n }\n\n if (strapi.contentTypes[parent.uid as keyof typeof strapi.contentTypes]) {\n // Found a content type parent\n return parent;\n }\n\n return null;\n};\n\n/**\n * Finds join table entries with publication state mismatches\n * Uses existing component parent detection from document service\n */\nconst findPublicationStateMismatches = async (\n db: Database,\n joinTableName: string,\n relation: any,\n targetModel: any,\n sourceModel: any\n): Promise => {\n try {\n // Get join column names using proper functions (addressing PR feedback)\n const sourceColumn = relation.joinTable.joinColumn.name;\n const targetColumn = relation.joinTable.inverseJoinColumn.name;\n\n // Get all join entries with their target entities\n const query = db\n .connection(joinTableName)\n .select(\n `${joinTableName}.id as join_id`,\n `${joinTableName}.${sourceColumn} as source_id`,\n `${joinTableName}.${targetColumn} as target_id`,\n `${targetModel.tableName}.published_at as target_published_at`\n )\n .leftJoin(\n targetModel.tableName,\n `${joinTableName}.${targetColumn}`,\n `${targetModel.tableName}.id`\n );\n\n const joinEntries = await query;\n\n // Group by source_id to find duplicates pointing to draft/published versions of same entity\n const entriesBySource: { [key: string]: any[] } = {};\n for (const entry of joinEntries) {\n const sourceId = entry.source_id;\n if (!entriesBySource[sourceId]) {\n entriesBySource[sourceId] = [];\n }\n entriesBySource[sourceId].push(entry);\n }\n\n const ghostEntries: number[] = [];\n\n // Check if this is a join table (ends with _lnk)\n const isRelationJoinTable = joinTableName.endsWith('_lnk');\n const isComponentModel =\n !sourceModel.uid?.startsWith('api::') &&\n !sourceModel.uid?.startsWith('plugin::') &&\n sourceModel.uid?.includes('.');\n\n // Check for draft/publish inconsistencies\n for (const [sourceId, entries] of Object.entries(entriesBySource)) {\n // Skip entries with single relations\n if (entries.length <= 1) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // For component join tables, check if THIS specific component instance's parent supports D&P\n if (isRelationJoinTable && isComponentModel) {\n try {\n const componentSchema = strapi.components[sourceModel.uid] as Schema.Component;\n if (!componentSchema) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const parent = await findContentTypeParentForComponentInstance(componentSchema, sourceId);\n if (!parent) {\n continue;\n }\n\n // Check if THIS component instance's parent supports draft/publish\n const parentContentType =\n strapi.contentTypes[parent.uid as keyof typeof strapi.contentTypes];\n if (!parentContentType?.options?.draftAndPublish) {\n // This component instance's parent does NOT support D&P - skip cleanup\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // If we reach here, this component instance's parent DOES support D&P\n // Continue to process this component instance for ghost relations\n } catch (error) {\n // Skip this component instance on error\n // eslint-disable-next-line no-continue\n continue;\n }\n }\n\n // Find ghost relations (same logic as original but with improved parent checking)\n for (const entry of entries) {\n if (entry.target_published_at === null) {\n // This is a draft target - find its published version\n const draftTarget = await db\n .connection(targetModel.tableName)\n .select('document_id')\n .where('id', entry.target_id)\n .first();\n\n if (draftTarget) {\n const publishedVersion = await db\n .connection(targetModel.tableName)\n .select('id', 'document_id')\n .where('document_id', draftTarget.document_id)\n .whereNotNull('published_at')\n .first();\n\n if (publishedVersion) {\n // Check if we also have a relation to the published version\n const publishedRelation = entries.find((e) => e.target_id === publishedVersion.id);\n if (publishedRelation) {\n ghostEntries.push(publishedRelation.join_id);\n }\n }\n }\n }\n }\n }\n\n return ghostEntries;\n } catch (error) {\n return [];\n }\n};\n"],"names":["cleanComponentJoinTable","db","joinTableName","relation","sourceModel","targetModel","metadata","get","target","logger","debug","sourceContentType","strapi","contentTypes","uid","sourceSupportsDraftPublish","options","draftAndPublish","targetContentType","targetSupportsDraftPublish","ghostEntries","findPublicationStateMismatches","length","connection","whereIn","del","error","errorMessage","Error","message","String","findContentTypeParentForComponentInstance","componentSchema","componentId","parentSchemas","getParentSchemasForComponent","parent","findComponentParent","components","parentComponentSchema","parentId","sourceColumn","joinTable","joinColumn","name","targetColumn","inverseJoinColumn","query","select","tableName","leftJoin","joinEntries","entriesBySource","entry","sourceId","source_id","push","isRelationJoinTable","endsWith","isComponentModel","startsWith","includes","entries","Object","parentContentType","target_published_at","draftTarget","where","target_id","first","publishedVersion","document_id","whereNotNull","publishedRelation","find","e","id","join_id"],"mappings":"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"}