UNPKG

@fontoxml/fontoxml-development-tools

Version:

Development tools for Fonto.

202 lines (185 loc) 5.11 kB
import asyncRouteWithLockCleanupHandler from '../asyncRouteWithLockCleanupHandler.js'; /** @typedef {import('../../src/getAppConfig.js').DevCmsConfig} DevCmsConfig */ /** @typedef {import('./stores/DevelopmentCms')} DevelopmentCms */ /** * @typedef DocumentToLoad * * @property {string} documentId * @property {boolean} [includeAdditionalDocuments] */ /** * @typedef Lock * * @property {boolean} isLockAcquired * @property {boolean} isLockAvailable */ /** * @typedef DocumentContext * * @property {{ [key: string]: unknown }} [documentMetadata] * @property {boolean} [isLockAcquired] */ /** * @typedef Document * * @property {string} documentId * @property {string} revisionId * @property {string} content * @property {Lock} lock * @property {{ [key: string]: unknown }} metadata * @property {DocumentContext} documentContext */ /** * @typedef DocumentErrorResult * * @property {string} documentId * @property {number} status */ /** * @typedef DocumentResult * * @property {string} documentId * @property {200} status * @property {Document} body */ /** * @param {string} documentId * @param {string} revisionId * @param {string} content * @param {Lock} lock * * @return {DocumentResult} */ function createDocumentResult(documentId, revisionId, content, lock) { return { documentId, status: 200, body: { documentId, revisionId, content, lock: { isLockAcquired: lock.isLockAcquired, isLockAvailable: lock.isLockAvailable, reason: !lock.isLockAvailable ? lock.lockReason : undefined, }, metadata: {}, documentContext: { documentMetadata: {}, isLockAcquired: lock.isLockAcquired, }, }, }; } /** * @param {DevCmsConfig} config */ function configureDocumentGetPostRouteHandler(config) { /** * @param {DocumentToLoad} documentToLoad * @param {string} editSessionToken * @param {DevelopmentCms} cms * @param {(filePath: string) => Promise<DevCmsFileLock>} acquireLock * * @return {Promise<DocumentResult | DocumentErrorResult>} */ async function loadDocument( documentToLoad, editSessionToken, cms, acquireLock, ) { const documentId = documentToLoad.documentId; let fileLock; try { fileLock = await acquireLock(documentId); const contentAndLatestRevisionId = await cms.getFileAndLatestRevisionId( documentId, editSessionToken, fileLock, ); if (!contentAndLatestRevisionId) { return { documentId, status: 404, }; } /** @type {Lock} */ const documentLoadLock = { ...config.documentLoadLock, ...config.documentLoadLockOverrides[documentId], }; return createDocumentResult( documentId, contentAndLatestRevisionId.revisionId, contentAndLatestRevisionId.content, documentLoadLock, ); } catch (_error) { return { documentId, status: 500, }; } finally { fileLock?.release(); } } return asyncRouteWithLockCleanupHandler(async (acquireLock, req, res) => { const editSessionToken = req.body?.context?.editSessionToken; // Collect all explicitly requested documents to load /** @type {DocumentToLoad[]} */ const documentsToLoad = []; const documentIds = new Set(); for (const documentToLoad of req.body.documents) { if ( typeof config.documentLoadBatchResultsLimit === 'number' && documentsToLoad.length >= config.documentLoadBatchResultsLimit ) { break; } if (documentIds.has(documentToLoad.documentId)) { continue; } documentsToLoad.push(documentToLoad); documentIds.add(documentToLoad.documentId); } // Collect all additional documents. const documentIdsWithAdditionalDocuments = new Set(); const addAdditionalDocumentsForDocument = (documentId) => { if (!config.additionalDocuments?.[documentId]) { return; } for (const additionalDocumentId of config.additionalDocuments[ documentId ]) { if (!documentIds.has(additionalDocumentId)) { // Add the document if it is not yet added. documentsToLoad.push({ documentId: additionalDocumentId }); documentIds.add(additionalDocumentId); } if (!documentIdsWithAdditionalDocuments.has(additionalDocumentId)) { // Recurse the additional document, if not already done so. addAdditionalDocumentsForDocument(additionalDocumentId); documentIdsWithAdditionalDocuments.add(additionalDocumentId); } } }; for (const documentToLoad of documentsToLoad) { if (!documentToLoad.includeAdditionalDocuments) { continue; } addAdditionalDocumentsForDocument(documentToLoad.documentId); } /** @type {(DocumentResult | DocumentErrorResult)[]} */ const results = await Promise.all( documentsToLoad.map((documentToLoad) => loadDocument(documentToLoad, editSessionToken, req.cms, acquireLock), ), ); res .status(200) .set('content-type', 'application/json; charset=utf-8') .json({ results }); }); } export default configureDocumentGetPostRouteHandler;