UNPKG

graphile-settings

Version:
120 lines (119 loc) 6.07 kB
import { safeInflection } from './inflection-utils'; import { buildForeignKeyConstraint, } from './constraint-meta-builders'; import { buildFieldList } from './table-meta-context'; import { getRelation, getResourceCodec, getUniques, sameAttributes, } from './table-resource-utils'; function isRecord(value) { return typeof value === 'object' && value !== null; } export function buildBelongsToRelations(codec, attributes, uniques, relations, context) { const belongsTo = []; for (const [relationName, relation] of Object.entries(relations)) { // PostGraphile only sets isReferencee when true (reverse FK); // forward relations have isReferencee undefined, not false. if (relation.isReferencee) continue; const localAttributes = relation.localAttributes || []; const isUnique = uniques.some((unique) => sameAttributes(unique.attributes, localAttributes)); const remoteCodec = relation.remoteResource?.codec; belongsTo.push({ fieldName: relationName, isUnique, type: remoteCodec?.name || null, keys: buildFieldList(localAttributes, codec, attributes, context), references: { name: remoteCodec?.name || 'unknown' }, }); } return belongsTo; } export function buildReverseRelations(codec, attributes, relations, context) { const hasOne = []; const hasMany = []; for (const [relationName, relation] of Object.entries(relations)) { if (relation.isReferencee !== true) continue; const remoteUniques = getUniques(relation.remoteResource || {}); const remoteAttributes = relation.remoteAttributes || []; const isUnique = remoteUniques.some((unique) => sameAttributes(unique.attributes, remoteAttributes)); const remoteCodec = relation.remoteResource?.codec; const meta = { fieldName: relationName, isUnique, type: remoteCodec?.name || null, keys: buildFieldList(relation.localAttributes || [], codec, attributes, context), referencedBy: { name: remoteCodec?.name || 'unknown' }, }; if (isUnique) { hasOne.push(meta); } else { hasMany.push(meta); } } return { hasOne, hasMany }; } function isManyToManyDetails(value) { if (!isRecord(value)) return false; return (isRecord(value.leftTable) && isRecord(value.junctionTable) && isRecord(value.rightTable) && typeof value.leftRelationName === 'string' && typeof value.rightRelationName === 'string'); } function parseManyToManyRelationships(value) { if (!Array.isArray(value)) return []; return value.filter(isManyToManyDetails); } function getManyToManyRelationships(build, tableResource, codec) { const relationshipsByResource = build.pgManyToManyRealtionshipsByResource; if (!(relationshipsByResource instanceof Map)) return []; const direct = parseManyToManyRelationships(relationshipsByResource.get(tableResource)); if (direct.length > 0) return direct; for (const [leftTable, relationships] of relationshipsByResource.entries()) { const details = parseManyToManyRelationships(relationships); if (details.length === 0 || !isRecord(leftTable)) continue; if (leftTable.codec === codec) { return details; } } return []; } function buildManyToManyRelation(details, context) { const leftCodec = getResourceCodec(details.leftTable); const junctionCodec = getResourceCodec(details.junctionTable); const rightCodec = getResourceCodec(details.rightTable); if (!leftCodec || !junctionCodec || !rightCodec) return null; const leftRelation = getRelation(details.leftTable, details.leftRelationName); const junctionRightRelation = getRelation(details.junctionTable, details.rightRelationName); if (!leftRelation || !junctionRightRelation) return null; const leftJunctionAttributes = leftRelation.remoteAttributes || []; const leftTableAttributes = leftRelation.localAttributes || []; const rightJunctionAttributes = junctionRightRelation.localAttributes || []; const rightTableAttributes = junctionRightRelation.remoteAttributes || []; const relationFieldName = safeInflection(() => context.build.inflection._manyToManyRelation?.(details), details.rightRelationName || rightCodec.name || null); const junctionLeftConstraint = buildForeignKeyConstraint(details.leftRelationName || `${junctionCodec.name}_${leftCodec.name}_fkey`, junctionCodec, junctionCodec.attributes, leftJunctionAttributes, leftCodec, leftCodec.attributes, leftTableAttributes, context); const junctionRightConstraint = buildForeignKeyConstraint(details.rightRelationName || `${junctionCodec.name}_${rightCodec.name}_fkey`, junctionCodec, junctionCodec.attributes, rightJunctionAttributes, rightCodec, rightCodec.attributes, rightTableAttributes, context); return { fieldName: relationFieldName, type: rightCodec.name || null, junctionTable: { name: junctionCodec.name || 'unknown' }, junctionLeftConstraint, junctionLeftKeyAttributes: buildFieldList(leftJunctionAttributes, junctionCodec, junctionCodec.attributes, context), junctionRightConstraint, junctionRightKeyAttributes: buildFieldList(rightJunctionAttributes, junctionCodec, junctionCodec.attributes, context), leftKeyAttributes: buildFieldList(leftTableAttributes, leftCodec, leftCodec.attributes, context), rightKeyAttributes: buildFieldList(rightTableAttributes, rightCodec, rightCodec.attributes, context), rightTable: { name: rightCodec.name || 'unknown' }, }; } export function buildManyToManyRelations(resource, codec, context) { return getManyToManyRelationships(context.build, resource, codec) .map((details) => buildManyToManyRelation(details, context)) .filter((relation) => relation !== null); }