payload
Version:
Node, React, Headless CMS and Application Framework built on Next.js
253 lines (252 loc) • 10.5 kB
JavaScript
// @ts-strict-ignore
import executeAccess from '../../auth/executeAccess.js';
import { combineQueries } from '../../database/combineQueries.js';
import { validateQueryPaths } from '../../database/queryValidation/validateQueryPaths.js';
import { sanitizeJoinQuery } from '../../database/sanitizeJoinQuery.js';
import { afterRead } from '../../fields/hooks/afterRead/index.js';
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js';
import { killTransaction } from '../../utilities/killTransaction.js';
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js';
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js';
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js';
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js';
import { getQueryDraftsSort } from '../../versions/drafts/getQueryDraftsSort.js';
import { buildAfterOperation } from './utils.js';
const lockDurationDefault = 300 // Default 5 minutes in seconds
;
export const findOperation = async (incomingArgs)=>{
let args = incomingArgs;
try {
// /////////////////////////////////////
// beforeOperation - Collection
// /////////////////////////////////////
if (args.collection.config.hooks?.beforeOperation?.length) {
for (const hook of args.collection.config.hooks.beforeOperation){
args = await hook({
args,
collection: args.collection.config,
context: args.req.context,
operation: 'read',
req: args.req
}) || args;
}
}
const { collection: { config: collectionConfig }, collection, currentDepth, depth, disableErrors, draft: draftsEnabled, includeLockStatus, joins, limit, overrideAccess, page, pagination = true, populate, req: { fallbackLocale, locale, payload }, req, select: incomingSelect, showHiddenFields, sort, where } = args;
const select = sanitizeSelect({
forceSelect: collectionConfig.forceSelect,
select: incomingSelect
});
// /////////////////////////////////////
// Access
// /////////////////////////////////////
let accessResult;
if (!overrideAccess) {
accessResult = await executeAccess({
disableErrors,
req
}, collectionConfig.access.read);
// If errors are disabled, and access returns false, return empty results
if (accessResult === false) {
return {
docs: [],
hasNextPage: false,
hasPrevPage: false,
limit,
nextPage: null,
page: 1,
pagingCounter: 1,
prevPage: null,
totalDocs: 0,
totalPages: 1
};
}
}
// /////////////////////////////////////
// Find
// /////////////////////////////////////
const usePagination = pagination && limit !== 0;
const sanitizedLimit = limit ?? (usePagination ? 10 : 0);
const sanitizedPage = page || 1;
let result;
let fullWhere = combineQueries(where, accessResult);
const sanitizedJoins = await sanitizeJoinQuery({
collectionConfig,
joins,
overrideAccess,
req
});
if (collectionConfig.versions?.drafts && draftsEnabled) {
fullWhere = appendVersionToQueryKey(fullWhere);
await validateQueryPaths({
collectionConfig: collection.config,
overrideAccess,
req,
versionFields: buildVersionCollectionFields(payload.config, collection.config, true),
where: appendVersionToQueryKey(where)
});
result = await payload.db.queryDrafts({
collection: collectionConfig.slug,
joins: req.payloadAPI === 'GraphQL' ? false : sanitizedJoins,
limit: sanitizedLimit,
locale,
page: sanitizedPage,
pagination: usePagination,
req,
select: getQueryDraftsSelect({
select
}),
sort: getQueryDraftsSort({
collectionConfig,
sort
}),
where: fullWhere
});
} else {
await validateQueryPaths({
collectionConfig,
overrideAccess,
req,
where
});
result = await payload.db.find({
collection: collectionConfig.slug,
draftsEnabled,
joins: req.payloadAPI === 'GraphQL' ? false : sanitizedJoins,
limit: sanitizedLimit,
locale,
page: sanitizedPage,
pagination,
req,
select,
sort,
where: fullWhere
});
}
if (includeLockStatus) {
try {
const lockDocumentsProp = collectionConfig?.lockDocuments;
const lockDuration = typeof lockDocumentsProp === 'object' ? lockDocumentsProp.duration : lockDurationDefault;
const lockDurationInMilliseconds = lockDuration * 1000;
const now = new Date().getTime();
const lockedDocuments = await payload.find({
collection: lockedDocumentsCollectionSlug,
depth: 1,
limit: sanitizedLimit,
overrideAccess: false,
pagination: false,
req,
where: {
and: [
{
'document.relationTo': {
equals: collectionConfig.slug
}
},
{
'document.value': {
in: result.docs.map((doc)=>doc.id)
}
},
// Query where the lock is newer than the current time minus lock time
{
updatedAt: {
greater_than: new Date(now - lockDurationInMilliseconds)
}
}
]
}
});
const lockedDocs = Array.isArray(lockedDocuments?.docs) ? lockedDocuments.docs : [];
// Filter out stale locks
const validLockedDocs = lockedDocs.filter((lock)=>{
const lastEditedAt = new Date(lock?.updatedAt).getTime();
return lastEditedAt + lockDurationInMilliseconds > now;
});
for (const doc of result.docs){
const lockedDoc = validLockedDocs.find((lock)=>lock?.document?.value === doc.id);
doc._isLocked = !!lockedDoc;
doc._userEditing = lockedDoc ? lockedDoc?.user?.value : null;
}
} catch (_err) {
for (const doc of result.docs){
doc._isLocked = false;
doc._userEditing = null;
}
}
}
// /////////////////////////////////////
// beforeRead - Collection
// /////////////////////////////////////
if (collectionConfig?.hooks?.beforeRead?.length) {
result.docs = await Promise.all(result.docs.map(async (doc)=>{
let docRef = doc;
for (const hook of collectionConfig.hooks.beforeRead){
docRef = await hook({
collection: collectionConfig,
context: req.context,
doc: docRef,
query: fullWhere,
req
}) || docRef;
}
return docRef;
}));
}
// /////////////////////////////////////
// afterRead - Fields
// /////////////////////////////////////
result.docs = await Promise.all(result.docs.map(async (doc)=>afterRead({
collection: collectionConfig,
context: req.context,
currentDepth,
depth,
doc,
draft: draftsEnabled,
fallbackLocale,
findMany: true,
global: null,
locale,
overrideAccess,
populate,
req,
select,
showHiddenFields
})));
// /////////////////////////////////////
// afterRead - Collection
// /////////////////////////////////////
if (collectionConfig?.hooks?.afterRead?.length) {
result.docs = await Promise.all(result.docs.map(async (doc)=>{
let docRef = doc;
for (const hook of collectionConfig.hooks.afterRead){
docRef = await hook({
collection: collectionConfig,
context: req.context,
doc: docRef,
findMany: true,
query: fullWhere,
req
}) || doc;
}
return docRef;
}));
}
// /////////////////////////////////////
// afterOperation - Collection
// /////////////////////////////////////
result = await buildAfterOperation({
args,
collection: collectionConfig,
operation: 'find',
result
});
// /////////////////////////////////////
// Return results
// /////////////////////////////////////
return result;
} catch (error) {
await killTransaction(args.req);
throw error;
}
};
//# sourceMappingURL=find.js.map