@fontoxml/fontoxml-development-tools
Version:
Development tools for Fonto.
131 lines (116 loc) • 3.62 kB
JavaScript
import asyncRouteWithLockCleanupHandler from '../asyncRouteWithLockCleanupHandler.js';
/** @typedef {import('../../src/getAppConfig.js').DevCmsConfig} DevCmsConfig */
function sendJson(res, status, body) {
res
.status(status)
.set('content-type', 'application/json; charset=utf-8')
.json(body);
}
/**
* @param {DevCmsConfig} config
*/
export default function configureDocumentLockPutRouteHandler(config) {
return asyncRouteWithLockCleanupHandler(async (acquireLock, req, res) => {
const documentId = req.body.documentId;
const revisionId = req.body.revisionId;
const lock = req.body.lock;
const documentContext = req.body.documentContext || {};
const fileLock = await acquireLock(documentId);
const contentAndLatestRevisionId = await req.cms.getFileAndLatestRevisionId(
documentId,
req.body.context.editSessionToken,
fileLock,
);
if (!contentAndLatestRevisionId) {
fileLock.release();
res.status(404).end();
return;
}
const documentLoadLock = {
...config.documentLoadLock,
...config.documentLoadLockOverrides[documentId],
};
const currentState = {
lock: {
isLockAcquired:
documentContext.isLockAcquired !== undefined
? documentLoadLock.isLockAvailable && documentContext.isLockAcquired
: documentLoadLock.isLockAcquired,
isLockAvailable: documentLoadLock.isLockAvailable,
reason: !documentLoadLock.isLockAvailable
? documentLoadLock.lockReason
: undefined,
},
revisionId: contentAndLatestRevisionId.revisionId,
};
if (!currentState.lock.isLockAvailable) {
// Sending 403 here would cause the document to be considered inaccessible
// instead of merely having an unavailable lock.
fileLock.release();
sendJson(res, 412, currentState);
return;
}
if (revisionId !== contentAndLatestRevisionId.revisionId) {
fileLock.release();
sendJson(res, 412, currentState);
return;
}
// Store lock state on the client for simplicity
const newDocumentContext = {
// NOTE: documentMetadata might be undefined, that depends on the load/save. Keeping
// whatever the client sends.
...documentContext,
isLockAcquired: lock.isLockAcquired,
};
const shouldGenerateAdditionalRevisionId = lock.isLockAcquired
? config.additionalRevisionIdOn.lock
: config.additionalRevisionIdOn.unlock;
if (!shouldGenerateAdditionalRevisionId) {
fileLock.release();
sendJson(res, 200, {
documentContext: newDocumentContext,
lock: {
...currentState.lock,
isLockAcquired: lock.isLockAcquired,
},
revisionId: contentAndLatestRevisionId.revisionId,
});
return;
}
const currentSession = req.getFontoSession(
req.body.context.editSessionToken,
);
let updatedRevisionId;
try {
updatedRevisionId = await req.cms.updateFile(
documentId,
contentAndLatestRevisionId.content,
currentSession.user,
currentSession.editSessionToken,
fileLock,
);
} catch (error) {
if (error.statusCode === 404) {
// Should not happen, as we already checked the existence of the document.
fileLock.release();
res.status(404).end();
return;
}
throw error;
}
fileLock.release();
if (contentAndLatestRevisionId.revisionId === updatedRevisionId) {
throw new Error(
`A new revision id was not generated as expected. The revision id remains: ${contentAndLatestRevisionId.revisionId}".`,
);
}
sendJson(res, 200, {
documentContext: newDocumentContext,
lock: {
...currentState.lock,
isLockAcquired: lock.isLockAcquired,
},
revisionId: updatedRevisionId,
});
});
}