UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

139 lines (138 loc) 6.46 kB
import { useEnv } from '@directus/env'; import { toArray } from '@directus/utils'; import { clone, isArray } from 'lodash-es'; export function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode, fieldAllowed) { const env = useEnv(); const nestedItems = toArray(nestedItem); const parentItems = clone(toArray(parentItem)); if (nestedNode.type === 'm2o') { const parentsByForeignKey = new Map(); // default all nested nodes to null for (const parentItem of parentItems) { const relationKey = parentItem[nestedNode.relation.field]; if (!parentsByForeignKey.has(relationKey)) { parentsByForeignKey.set(relationKey, []); } parentItem[nestedNode.fieldKey] = null; parentsByForeignKey.get(relationKey).push(parentItem); } const nestedPrimaryKeyField = schema.collections[nestedNode.relation.related_collection].primary; // populate nested items where applicable for (const nestedItem of nestedItems) { const nestedPK = nestedItem[nestedPrimaryKeyField]; // user has no access to the nestedItem PK field if (nestedPK === null) { continue; } // Existing M2O record (i.e. valid nested PK) but not linked to this (or any) parent item if (!parentsByForeignKey.has(nestedPK)) { continue; } for (const parentItem of parentsByForeignKey.get(nestedPK)) { parentItem[nestedNode.fieldKey] = nestedItem; } } } else if (nestedNode.type === 'o2m') { const parentCollectionName = nestedNode.relation.related_collection; const parentPrimaryKeyField = schema.collections[parentCollectionName].primary; const parentRelationField = nestedNode.fieldKey; const nestedParentKeyField = nestedNode.relation.field; const parentsByPrimaryKey = new Map(); for (const parentItem of parentItems) { if (!parentItem[parentRelationField]) { parentItem[parentRelationField] = []; } let parentPrimaryKey = parentItem[parentPrimaryKeyField]; // null if the user has no access to parent PK if (parentPrimaryKey === null) { continue; } else { // ensure key access is type agnostic parentPrimaryKey = parentPrimaryKey.toString(); } if (parentsByPrimaryKey.has(parentPrimaryKey)) { throw new Error(`Duplicate parent primary key '${parentPrimaryKey}' of '${parentCollectionName}' when merging o2m nested items`); } parentsByPrimaryKey.set(parentPrimaryKey, parentItem); } const toAddToAllParents = []; for (const nestedItem of nestedItems) { if (nestedItem === null) { continue; } if (Array.isArray(nestedItem[nestedParentKeyField])) { toAddToAllParents.push(nestedItem); // TODO explain this odd case continue; // Avoids adding the nestedItem twice } const parentPrimaryKey = nestedItem[nestedParentKeyField]?.[parentPrimaryKeyField] ?? nestedItem[nestedParentKeyField]; if (parentPrimaryKey === null) { continue; } const parentItem = parentsByPrimaryKey.get(parentPrimaryKey.toString()); // null if the user has no access to parent PK if (!parentItem) { continue; } parentItem[parentRelationField].push(nestedItem); } for (const [index, parentItem] of parentItems.entries()) { if (fieldAllowed === false || (isArray(fieldAllowed) && !fieldAllowed[index])) { parentItem[nestedNode.fieldKey] = null; continue; } parentItem[parentRelationField].push(...toAddToAllParents); const limit = nestedNode.query.limit ?? Number(env['QUERY_LIMIT_DEFAULT']); if (nestedNode.query.page && nestedNode.query.page > 1) { parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(limit * (nestedNode.query.page - 1)); } if (nestedNode.query.offset && nestedNode.query.offset >= 0) { parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(nestedNode.query.offset); } if (limit !== -1) { parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, limit); } parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].sort((a, b) => { // This is pre-filled in get-ast-from-query const sortField = nestedNode.query.sort[0]; let column = sortField; let order = 'asc'; if (sortField.startsWith('-')) { column = sortField.substring(1); order = 'desc'; } if (a[column] === b[column]) return 0; if (a[column] === null) return 1; if (b[column] === null) return -1; if (order === 'asc') { return a[column] < b[column] ? -1 : 1; } else { return a[column] < b[column] ? 1 : -1; } }); } } else if (nestedNode.type === 'a2o') { for (const parentItem of parentItems) { if (!nestedNode.relation.meta?.one_collection_field) { parentItem[nestedNode.fieldKey] = null; continue; } const relatedCollection = parentItem[nestedNode.relation.meta.one_collection_field]; if (!nestedItem[relatedCollection]) { parentItem[nestedNode.fieldKey] = null; continue; } const itemChild = nestedItem[relatedCollection].find((nestedItem) => { return nestedItem[nestedNode.relatedKey[relatedCollection]] == parentItem[nestedNode.fieldKey]; }); parentItem[nestedNode.fieldKey] = itemChild || null; } } return Array.isArray(parentItem) ? parentItems : parentItems[0]; }