UNPKG

@paroicms/server

Version:
258 lines 9.25 kB
import { documentTypeHasData, getDocumentTypeByName, getNodeTypeByName, } from "@paroicms/internal-anywhere-lib"; import { encodeLNodeId } from "@paroicms/public-anywhere-lib"; import { ApiError, createSimpleTranslator } from "@paroicms/public-server-lib"; import { type } from "arktype"; import { applyRegularChildrenSortingOnQuery } from "../../common/child-ordering-query.js"; import { invalidateDocumentInCache, invalidateDocumentRelationCache, } from "../../common/text-cache.js"; import { searchText } from "../../public-api/search-text/search-text.queries.js"; import { getParentLNodeIdOf } from "../document/load-documents.queries.js"; import { getNodeAncestorsForBreadcrumb } from "../node/node-cte.queries.js"; import { getTypeNameOf } from "../node/node.queries.js"; import { getDocumentNodeIdOfPart } from "../part/part.queries.js"; const FindOneLNodeRowAT = type({ language: "string", nodeId: "number", ready: "number", "+": "reject", }).pipe((data) => ({ language: data.language, nodeId: String(data.nodeId), ready: Boolean(data.ready), })); export async function findOneLNode({ cn }, lNodeId) { const found = await cn("PaLNode as s") .select(["s.language", "s.nodeId", "s.ready"]) .where("s.nodeId", lNodeId.nodeId) .andWhere("s.language", lNodeId.language) .first(); if (!found) { throw new ApiError(`can't find lNode ${lNodeId.language}:${lNodeId.nodeId}`, 404); } const lNode = FindOneLNodeRowAT.assert(found); return lNodeWrapToObjectType(lNode); } export async function getLNodeWraps(siteContext, { parentId, pagination, orderBy, }) { let total; let lNodeEntities; if (pagination) { const childLNodesResult = await getChildLNodesForAllLanguages(siteContext, { parentNodeId: parentId.nodeId, pagination, orderBy, }); lNodeEntities = childLNodesResult.list; total = childLNodesResult.total; } else { lNodeEntities = await getChildLNodesForAllLanguages(siteContext, { parentNodeId: parentId.nodeId, pagination, orderBy, }); } const byNodeIdMap = new Map(); lNodeEntities.forEach((lNodeEntity) => { let list = byNodeIdMap.get(lNodeEntity.nodeId); if (!list) { list = []; byNodeIdMap.set(lNodeEntity.nodeId, list); } list.push(lNodeEntity); }); const result = []; byNodeIdMap.forEach((list) => { let found = list.find((i) => i.language === parentId.language); found ??= list[0]; result.push(found); }); return { total, lNodeWraps: result.map(lNodeWrapToObjectType), }; } function lNodeWrapToObjectType(lNode) { const { language, nodeId } = lNode; return { lNodeId: encodeLNodeId({ language, nodeId }), language, nodeId, ready: lNode.ready, }; } const ChildLNodesRowAT = type({ nodeId: "number", language: "string", ready: "number", "+": "reject", }).pipe((data) => ({ nodeId: String(data.nodeId), language: data.language, ready: Boolean(data.ready), })); const ChildLNodesCountRowAT = type({ cnt: "number", "+": "reject", }); async function getChildLNodesForAllLanguages(siteContext, options) { const parentTypeName = await getTypeNameOf(siteContext, options.parentNodeId); const parentDocumentType = getDocumentTypeByName(siteContext.siteSchema, parentTypeName); const query = createBaseChildLNodesForAllLanguagesQueryBuilder(siteContext, { parentNodeId: options.parentNodeId, typeNames: parentDocumentType.regularChildren ?? [], }).select(["s.nodeId", "s.language", "s.ready"]); applyRegularChildrenSortingOnQuery(siteContext, { query, parentDocumentType, leftJoinDocument: true, orderBy: options.orderBy, }); if (options.pagination) { query.limit(options.pagination.limit).offset(options.pagination.start); } const rows = await query; const formattedRows = rows.map((row) => ChildLNodesRowAT.assert(row)); if (options.pagination) { let total; if (rows.length < options.pagination.limit) { total = options.pagination.start + rows.length; } else { const countQuery = createBaseChildLNodesForAllLanguagesQueryBuilder(siteContext, { parentNodeId: options.parentNodeId, typeNames: parentDocumentType.regularChildren ?? [], }); const countResult = await countQuery.count("* as cnt").first(); total = countResult ? ChildLNodesCountRowAT.assert(countResult).cnt : 0; } return { list: formattedRows, total, }; } return formattedRows; } function createBaseChildLNodesForAllLanguagesQueryBuilder(siteContext, { parentNodeId, typeNames, }) { return siteContext .cn("PaLNode as s") .innerJoin("PaNode as l", "l.id", "s.nodeId") .where("l.parentId", parentNodeId) .whereIn("l.typeName", typeNames); } export async function setLNodeReady(siteContext, { lNodeId, ready }) { const typeName = await getTypeNameOf(siteContext, lNodeId.nodeId); const nodeType = getNodeTypeByName(siteContext.siteSchema, typeName); if (nodeType.kind !== "document" && nodeType.kind !== "part") { throw new Error(`invalid lNode type '${typeName}'`); } await siteContext.cn("PaLNode").update({ ready }).where({ nodeId: lNodeId.nodeId, language: lNodeId.language, }); if (nodeType.kind === "document") { const parentId = await getParentLNodeIdOf(siteContext, lNodeId); if (ready) { if (parentId) { await invalidateDocumentRelationCache(siteContext, { documentId: parentId, relation: "children", }); } } else { await invalidateDocumentInCache(siteContext, { documentId: lNodeId, fully: true, parentId, }); } } else { const documentId = { nodeId: await getDocumentNodeIdOfPart(siteContext, lNodeId.nodeId), language: lNodeId.language, }; await invalidateDocumentInCache(siteContext, { documentId }); } } export async function getBreadcrumb(siteContext, tracker, documentId, options = {}) { const { siteSchema, logger } = siteContext; const schemaI18n = createSimpleTranslator({ labels: siteSchema.l10n, logger }); const ancestors = await getNodeAncestorsForBreadcrumb(siteContext, tracker, documentId, { ensurePublished: options.ensurePublished, }); ancestors.reverse(); if (options.skipHome) { ancestors.shift(); } if (options.skipCurrent) { ancestors.pop(); } const result = []; for (const ancestor of ancestors) { const documentType = getDocumentTypeByName(siteContext.siteSchema, ancestor.typeName); const hasData = documentTypeHasData(documentType); if (options.ensurePublished && !hasData) continue; result.push({ documentId: { nodeId: ancestor.nodeId, language: documentId.language }, typeName: ancestor.typeName, title: hasData ? ancestor.title : schemaI18n.translate({ key: `nodeTypes.${documentType.typeName}.label`, language: documentId.language, defaultValue: documentType.typeName, }), }); } return result; } const OrphanNodeRowAT = type({ cnt: "number", "+": "reject", }); export async function isOrphanNode(cn, nodeId) { const row = (await cn("PaLNode as s") .count("s.nodeId as cnt") .where("s.nodeId", nodeId) .first()); return row ? OrphanNodeRowAT.assert(row).cnt === 0 : true; } export async function searchDocuments(siteContext, options) { const { queryString, language, start, limit } = options; const words = queryString.split(/\s+/).filter((word) => word.length >= 2); if (words.length === 0) return { total: 0, start, items: [] }; const { rows, total } = await searchText(siteContext, { language, words, start, limit, orderBy: options.orderBy, }); const lNodeWraps = []; for (const row of rows) { lNodeWraps.push(await findOneLNode(siteContext, { language: row.language, nodeId: row.nodeId, })); } return { total, items: lNodeWraps, start, }; } const LNodeLanguageRowAT = type({ language: "string", "+": "reject", }); export async function getParentLanguages(siteContext, nodeId) { const parentLanguages = await siteContext .cn("PaLNode as p") .select("p.language") .innerJoin("PaNode as c", "c.parentId", "p.nodeId") .where("c.id", nodeId); return parentLanguages.map((item) => LNodeLanguageRowAT.assert(item).language); } //# sourceMappingURL=lnode.queries.js.map