UNPKG

@directus/api

Version:

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

112 lines (110 loc) 4.43 kB
import { applyFunctionToColumnName } from "./utils/apply-function-to-column-name.js"; import { extractFunctionName } from "../../utils/extract-function-name.js"; import database_default from "../index.js"; import { fetchPolicies } from "../../permissions/lib/fetch-policies.js"; import { fetchPermissions } from "../../permissions/lib/fetch-permissions.js"; import { getDBQuery } from "./lib/get-db-query.js"; import { parseCurrentLevel } from "./lib/parse-current-level.js"; import { PayloadService } from "../../services/payload.js"; import { applyParentFilters } from "./utils/apply-parent-filters.js"; import { mergeWithParentItems } from "./utils/merge-with-parent-items.js"; import { removeTemporaryFields } from "./utils/remove-temporary-fields.js"; import { useEnv } from "@directus/env"; import { cloneDeep, merge } from "lodash-es"; //#region src/database/run-ast/run-ast.ts /** * Execute a given AST using Knex. Returns array of items based on requested AST. */ async function runAst(originalAST, schema, accountability, options) { const ast = cloneDeep(originalAST); const knex = options?.knex || database_default(); if (ast.type === "a2o") { const results = {}; for (const collection of ast.names) results[collection] = await run(collection, ast.children[collection], ast.query[collection], ast.cases[collection] ?? [], accountability); return results; } else return await run(ast.name, ast.children, options?.query || ast.query, ast.cases, accountability); async function run(collection, children, query, cases, accountability$1) { const env = useEnv(); const { fieldNodes, primaryKeyField, nestedCollectionNodes } = await parseCurrentLevel(schema, collection, children, query); const o2mNodes = nestedCollectionNodes.filter((node) => node.type === "o2m"); let permissions = []; if (accountability$1 && !accountability$1.admin) permissions = await fetchPermissions({ action: "read", accountability: accountability$1, policies: await fetchPolicies(accountability$1, { schema, knex }) }, { schema, knex }); const rawItems = await getDBQuery({ table: collection, fieldNodes, o2mNodes, query, cases, permissions }, { schema, knex }); if (!rawItems) return null; const payloadService = new PayloadService(collection, { accountability: accountability$1, knex, schema }); const aliasMap = { ...query.alias }; for (const child of children) if (child.type === "functionField" && extractFunctionName(child.name) === "json") { const alias = applyFunctionToColumnName(child.fieldKey); aliasMap[alias] = child.name; } let items = await payloadService.processValues("read", rawItems, aliasMap, query.aggregate ?? {}); if (!items || Array.isArray(items) && items.length === 0) return items; const nestedNodes = applyParentFilters(schema, nestedCollectionNodes, items); for (const nestedNode of nestedNodes) { let nestedItems = []; if (nestedNode.type === "o2m") { let hasMore = true; let batchCount = 0; const hasWhenCase = nestedNode.whenCase && nestedNode.whenCase.length > 0; let fieldAllowed = true; if (hasWhenCase) if (Array.isArray(items)) { fieldAllowed = []; for (const item of items) { fieldAllowed.push(!!item[nestedNode.fieldKey]); delete item[nestedNode.fieldKey]; } } else { fieldAllowed = !!items[nestedNode.fieldKey]; delete items[nestedNode.fieldKey]; } while (hasMore) { nestedItems = await runAst(merge({}, nestedNode, { query: { limit: env["RELATIONAL_BATCH_SIZE"], offset: batchCount * env["RELATIONAL_BATCH_SIZE"], page: null } }), schema, accountability$1, { knex, nested: true }); if (nestedItems) items = mergeWithParentItems(schema, nestedItems, items, nestedNode, fieldAllowed); if (!nestedItems || nestedItems.length < env["RELATIONAL_BATCH_SIZE"]) hasMore = false; batchCount++; } } else { nestedItems = await runAst(merge({}, nestedNode, { query: { limit: -1 } }), schema, accountability$1, { knex, nested: true }); if (nestedItems) items = mergeWithParentItems(schema, nestedItems, items, nestedNode, true); } } if (options?.nested !== true && options?.stripNonRequested !== false) items = removeTemporaryFields(schema, items, originalAST, primaryKeyField); return items; } } //#endregion export { runAst };