@paroicms/server
Version:
The ParoiCMS server
258 lines • 9.25 kB
JavaScript
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