@fontoxml/fontoxml-development-tools
Version:
Development tools for Fonto.
173 lines (158 loc) • 5.12 kB
JavaScript
import asyncRouteWithLockCleanupHandler from '../asyncRouteWithLockCleanupHandler.js';
/** @typedef {import('../../src/getAppConfig.js').DevCmsConfig} DevCmsConfig */
let warningAboutUnsupportedConfigurationWasLogged = false;
/**
* @param {DevCmsConfig} config
*/
export default function configureDocumentStatePostRouteHandler(config) {
return asyncRouteWithLockCleanupHandler(async (acquireLock, req, res) => {
const results = await Promise.all(
req.body.documents.map(async ({ documentContext, documentId }) => {
let fileLock;
let contentAndLatestRevisionId;
try {
fileLock = await acquireLock(documentId);
contentAndLatestRevisionId = await req.cms.getFileAndLatestRevisionId(
documentId,
req.body.context.editSessionToken,
fileLock,
);
} catch (error) {
fileLock?.release();
// NOTE: Status 500 is not supported per item. But be checked after all
// documents have been processed, and the whole request will return 500.
return {
status: 500,
error,
};
}
if (!contentAndLatestRevisionId) {
fileLock?.release();
return {
status: 404,
};
}
const documentLoadLock = {
...config.documentLoadLock,
...config.documentLoadLockOverrides[documentId],
};
const revisionIdOnUnlock = config.additionalRevisionIdOn.unlock;
if (
!warningAboutUnsupportedConfigurationWasLogged &&
revisionIdOnUnlock &&
documentLoadLock.releaseLocksOnStateRequest
) {
console.warn(
`\nGenerating additional revision id on 'unlock' while also having release locks on state request enabled. This will cause out of sync documents states.`,
);
warningAboutUnsupportedConfigurationWasLogged = true;
}
const wasLockAcquired = documentContext
? documentLoadLock.isLockAvailable && documentContext.isLockAcquired
: documentLoadLock.isLockAcquired;
if (
// No need to generate a new revision id if the document will not be
// released. Either due to configuration or due to it already being not
// acquired.
!revisionIdOnUnlock ||
!documentLoadLock.releaseLocksOnStateRequest ||
!wasLockAcquired
) {
const isLockAcquired = documentLoadLock.releaseLocksOnStateRequest
? false
: wasLockAcquired;
const newDocumentContext = {
// NOTE: documentMetadata might be undefined, that depends on the load/save. Keeping
// whatever the client sends.
...documentContext,
isLockAcquired,
};
fileLock?.release();
return {
status: 200,
body: {
documentContext: newDocumentContext,
revisionId: contentAndLatestRevisionId.revisionId,
lock: {
isLockAcquired,
isLockAvailable: documentLoadLock.isLockAvailable,
reason: !documentLoadLock.isLockAvailable
? documentLoadLock.lockReason
: undefined,
},
},
};
}
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;
}
if (contentAndLatestRevisionId.revisionId === updatedRevisionId) {
fileLock.release();
const error = new Error(
`A new revision id was not generated as expected. The revision id remains: ${contentAndLatestRevisionId.revisionId}".`,
);
// NOTE: Status 500 is not supported per item. But be checked after all
// documents have been processed, and the whole request will return 500.
return {
status: 500,
error,
};
}
const newDocumentContext = {
// NOTE: documentMetadata might be undefined, that depends on the load/save.
// Keeping whatever the client sends.
...documentContext,
isLockAcquired: false,
};
fileLock.release();
return {
status: 200,
body: {
documentContext: newDocumentContext,
revisionId: updatedRevisionId,
lock: {
isLockAcquired: false,
isLockAvailable: documentLoadLock.isLockAvailable,
reason: !documentLoadLock.isLockAvailable
? documentLoadLock.lockReason
: undefined,
},
},
};
}),
);
// The endpoint does not support a 500 status per result, so we will return a 500 on the
// whole request instead.
const firstResultWithError = results.find(
(result) => result.status === 500,
);
if (firstResultWithError) {
throw (
firstResultWithError.error ??
new Error('A result has a 500 status, which is not supported.')
);
}
res
.status(200)
.set('content-type', 'application/json; charset=utf-8')
.json({ results });
});
}